<?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: Bro Force</title>
    <description>The latest articles on DEV Community by Bro Force (@bro_force_af8dd8c3202a933).</description>
    <link>https://dev.to/bro_force_af8dd8c3202a933</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%2F4103222%2F3a86ccc0-c372-4b0a-8096-c38009e175d9.jpg</url>
      <title>DEV Community: Bro Force</title>
      <link>https://dev.to/bro_force_af8dd8c3202a933</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bro_force_af8dd8c3202a933"/>
    <language>en</language>
    <item>
      <title>I wrote a justification in my docs, then tested it. It was mostly wrong.</title>
      <dc:creator>Bro Force</dc:creator>
      <pubDate>Fri, 02 Oct 2026 07:41:45 +0000</pubDate>
      <link>https://dev.to/bro_force_af8dd8c3202a933/i-wrote-a-justification-in-my-docs-then-tested-it-it-was-mostly-wrong-1ibn</link>
      <guid>https://dev.to/bro_force_af8dd8c3202a933/i-wrote-a-justification-in-my-docs-then-tested-it-it-was-mostly-wrong-1ibn</guid>
      <description>&lt;p&gt;&lt;em&gt;Built for the &lt;a href="https://dev.to/partnerships_raptors"&gt;@partnerships_raptors&lt;/a&gt; Hackathon Raptors DOGFOOD hackathon: 72 hours, build the submission and judging platform that will judge you. FastAPI, PostgreSQL 16, Next.js, one &lt;code&gt;docker compose up&lt;/code&gt;. Code: &lt;a href="https://github.com/broforce6909-cmd/DogFood" rel="noopener noreferrer"&gt;github.com/broforce6909-cmd/DogFood&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My judging docs contained a table explaining why you must not put an epsilon in the denominator when you normalize a judge who gave every project the same score. It said the judge would "become the most influential voice in the event."&lt;/p&gt;

&lt;p&gt;After the build, I tested that sentence on the organizers' own data. It is false. The thing that actually mattered was something I had filed under "small detail." This post is about that, and about three other places where the problem was harder than the spec made it sound.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Normalizing judges: what actually moves the ranking
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The setup.&lt;/strong&gt; Judges do not share a scale. In the organizers' fixture, judge averages run from 2.0 to 4.22. I standardise each judge (&lt;code&gt;z = (score - judge_mean) / judge_sd&lt;/code&gt;), average z-scores per project, and rescale to 1-5. Two edge cases needed rules: a judge with zero spread, and a judge with very few ballots.&lt;/p&gt;

&lt;p&gt;My rules: a zero-spread judge maps to &lt;code&gt;z = 0&lt;/code&gt; ("I am not distinguishing between these"), and every judge's mean and sd are shrunk toward the field's with a constant &lt;code&gt;k = 3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The test.&lt;/strong&gt; I ran the real &lt;code&gt;scoring.py&lt;/code&gt; on the organizers' &lt;code&gt;fixtures.json&lt;/code&gt; (30 judges, 126 ballots, 41 projects before I dedupe a duplicate team; equal rubric weights assumed) and compared alternatives. Ranking agreement with what I shipped, as Kendall tau:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;tau&lt;/th&gt;
&lt;th&gt;Top-5 overlap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw mean, no normalization&lt;/td&gt;
&lt;td&gt;0.80&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plain z-score, epsilon in the denominator&lt;/td&gt;
&lt;td&gt;0.80&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shrinkage on, but a flat judge borrows the global sd&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shipped: flat judge = z 0, shrinkage k = 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;5/5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Finding 1: normalization matters.&lt;/strong&gt; 13 of 41 projects move three or more places versus the raw ranking, and raw third place is not normalized third place. Averaging raw scores across judges who use different scales gives a measurably different leaderboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 2: my epsilon argument was wrong.&lt;/strong&gt; An exactly flat judge has &lt;code&gt;score - mean = 0&lt;/code&gt;, so &lt;code&gt;0 / (0 + 1e-9) = 0&lt;/code&gt;. The epsilon version produced the same ranking as no shrinkage at all. It did not blow up. And the "flat judge" rule versus borrowing the global sd moved one project in the top ten. The rule is still the principled choice, but I had sold it as a disaster-prevention measure and it is not one on this data. Only 3 of 30 judges are flat, and two of those have a single ballot, which is flat by definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 3: the real hazard was the small-n judge.&lt;/strong&gt; For a judge with two ballots, population sd gives &lt;code&gt;z = +1&lt;/code&gt; for the higher and &lt;code&gt;z = -1&lt;/code&gt; for the lower, &lt;strong&gt;always&lt;/strong&gt;, whether the two scores differ by 0.33 or by 1.0. In the fixture, 8 of 30 judges have two or fewer ballots, and the six two-ballot judges' gaps range from 0.33 to 1.0. Without shrinkage each of them hands out the same-sized push regardless of how much they actually discriminated. That, not the flat judge, is what the plain z-score gets wrong (tau 0.80 again), and shrinkage is what fixes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 4, the disappointing one: &lt;code&gt;k&lt;/code&gt; is a hand-picked constant and the top five is sensitive to it.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;k&lt;/th&gt;
&lt;th&gt;Kendall tau vs k = 3&lt;/th&gt;
&lt;th&gt;Top 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;~0 (no shrinkage)&lt;/td&gt;
&lt;td&gt;0.80&lt;/td&gt;
&lt;td&gt;one project in, one out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1-5&lt;/td&gt;
&lt;td&gt;0.94-1.00&lt;/td&gt;
&lt;td&gt;identical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;0.94&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;third place drops out of the top five&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;further reshuffle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;∞ (everyone is average)&lt;/td&gt;
&lt;td&gt;0.89&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is a stable plateau around k = 1 to 5, and I picked k = 3 inside it. But there is no ground truth here, so I cannot tell you k = 3 is &lt;em&gt;right&lt;/em&gt;, only that it is not fragile nearby. It is published in the API response as &lt;code&gt;shrinkage_k&lt;/code&gt;. The honest claim is "inspectable and stable in a range," not "optimal." I chose a fixed constant over a hierarchical model because with a dozen judges and a few ballots each, the between-judge variance would itself be estimated from almost nothing. You would get a more impressive number with no more information in it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reproduce all of it with &lt;code&gt;normalization_sensitivity.py&lt;/code&gt; in the repo (no database needed).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A related catch.&lt;/strong&gt; My first version z-scored against one global pool. A judge restricted to one track was being compared against a mean that included tracks they never saw, so a track that simply drew stronger projects looked like a harsh judge, and normalization cannot tell those apart. &lt;code&gt;normalize_by_track()&lt;/code&gt; now partitions by the &lt;strong&gt;submission's&lt;/strong&gt; track (not the judge's, since an unrestricted judge spans tracks). A normalized 4.1 in one track and a 4.1 in another are still not the same claim, and I wrote that into the docs rather than hide it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Role isolation: make the bad request unexpressible
&lt;/h2&gt;

&lt;p&gt;The rule is one sentence: &lt;strong&gt;organizers may read every ballot and may not write one.&lt;/strong&gt; Everywhere else, organizer and admin can do anything to an event. Scoring is the single exception, because anyone who can author a judge's score can forge a result, and nothing downstream (export, normalization, audit log) could tell a forgery from a judgement.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;PUT /api/judging/assignments/{id}/scores&lt;/code&gt; has &lt;strong&gt;no "on behalf of" parameter at all.&lt;/strong&gt; Refusing a request is a check. Not being able to express it is a property. Checks drift; properties do not.&lt;/p&gt;

&lt;p&gt;The unit of isolation is the &lt;strong&gt;ballot&lt;/strong&gt; (a &lt;code&gt;judge_assignments&lt;/code&gt; row), not the user or the submission. Three requirements collapse into one ownership check: a judge cannot see a peer's scores, a track judge cannot see another track, and scoring closes outside the judging window. A &lt;code&gt;Score&lt;/code&gt; has no rules of its own, so it is readable exactly when its ballot is.&lt;/p&gt;

&lt;p&gt;One small decision worth copying: a peer's ballot returns &lt;strong&gt;403, not 404.&lt;/strong&gt; That a peer holds a ballot is not a secret (the organizer grid shows it), so pretending otherwise protects nothing. Another team's draft returns 404, because there the existence is the secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Check every feature's read side against its write side
&lt;/h2&gt;

&lt;p&gt;After all four tiers worked, I audited with one question per feature: &lt;em&gt;can a real user produce the state this code reads?&lt;/em&gt; Every one of these passed its own tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Voting configuration had no write path.&lt;/strong&gt; &lt;code&gt;voting_method&lt;/code&gt;, &lt;code&gt;vote_credits&lt;/code&gt;, &lt;code&gt;votes_per_voter&lt;/code&gt; and &lt;code&gt;voting_access&lt;/code&gt; were read by every voter route. Neither &lt;code&gt;EventCreate&lt;/code&gt; nor &lt;code&gt;EventUpdate&lt;/code&gt; had a field for them. A real organizer could not turn on quadratic voting; every event that used it was seeded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Six of ten webhook topics never fired.&lt;/strong&gt; &lt;code&gt;GET /webhooks/topics&lt;/code&gt; advertised all ten. Six were never passed to &lt;code&gt;hooks.schedule()&lt;/code&gt; anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificate lookup had no auth check.&lt;/strong&gt; Any participant could list any project's codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSV import skipped the stored-XSS validation&lt;/strong&gt; that the JSON API had. Two write paths to one column, one validator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is that I had tested what I built, not what a user could reach. Demo data hid all four.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The benchmark that lied
&lt;/h2&gt;

&lt;p&gt;My first load pass (200 submissions, 50 judges, ~1,000 ballots) found nothing alarming. I wrote "fine at hackathon scale." Then I seeded 1,200 submissions, 520 judges, 3,600 ballots, 54,600 votes and 110,000 chained audit entries, and checked in both the seeding and measuring scripts. Three real bottlenecks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The connection pool was the ceiling, not any query.&lt;/strong&gt; &lt;code&gt;create_engine()&lt;/code&gt; had no &lt;code&gt;pool_size&lt;/code&gt;, so SQLAlchemy's default of 5 + 10 = 15 connections applied to the whole process, while Starlette runs sync routes on a 40-thread pool. Request 16 was not CPU-starved; it was queued behind 14 holding connections. That is multi-second tail latency with zero errors, invisible below real concurrency, which is why pass one missed it. &lt;code&gt;pool_size=20, max_overflow=20&lt;/code&gt; took the public gallery's p99 from 4.8s to 1.2s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;verify_chain()&lt;/code&gt; hydrated 110,000 ORM objects&lt;/strong&gt; to read twelve plain fields each. A column-only select with &lt;code&gt;yield_per&lt;/code&gt; cut verification from about 13s to about 8s. The rest is 110,000 SHA-256 calls, which is what a hash chain costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The vote tally built a full ORM set on every poll&lt;/strong&gt; to produce a filter that narrowed nothing for most events. Skipping it cut concurrent p50 from roughly 11-13s to 4-7s.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Caveats I printed rather than rounded away: one laptop, and identical code varied about 2x between runs, so I report ranges. I did not cache the tally. Caching would be faster but would break the "computed on every request, never stale" property results depend on, which is a product decision, not a perf tweak.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I cut, and what I would redo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cut and do not regret:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expertise matching in assignment.&lt;/strong&gt; Z-scoring assumes judges see comparable slices of quality, so assignment is balanced, shuffled and conflict-free. Smarter matching would have made assignment better and normalization wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breaking ties.&lt;/strong&gt; Equal scores share a rank (1, 1, 3). Deciding first place on a float tie-break is worse than reporting a tie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A job scheduler.&lt;/strong&gt; So webhooks are not retried: five failures disable a hook. A queue is a dependency this stack does not need yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Would redo:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A judge-to-tracks table.&lt;/strong&gt; The organizers' file gives nine judges two tracks each; my judge record holds one or all, so they load as all-track judges. No loaded ballot changes, but a fresh assignment would treat them as unrestricted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cached, invalidated results.&lt;/strong&gt; &lt;code&gt;GET /results&lt;/code&gt; takes about 1.7s at 3,600 ballots because I compute it on every request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret ballots.&lt;/strong&gt; Organizers can see who voted for what. Fixing that makes one-vote-per-person unenforceable without blind signatures, so I named it as an open problem instead of faking it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repo has a section called "What it does not do yet," and in places it is longer than the feature list. I think that is the most useful part of any hackathon project: not what works, but exactly where it stops, and which of your own claims survived when you went back and tested them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Hackathon Raptors (&lt;a class="mentioned-user" href="https://dev.to/partnerships_raptors"&gt;@partnerships_raptors&lt;/a&gt;) for a brief worth building. This is my entry for the Write Up Quest.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>python</category>
      <category>postgres</category>
      <category>statistics</category>
    </item>
    <item>
      <title>I Gave My Secret Scanner a Confidence Score — Then Found a Number I Didn't Want to Publish</title>
      <dc:creator>Bro Force</dc:creator>
      <pubDate>Mon, 31 Aug 2026 19:12:40 +0000</pubDate>
      <link>https://dev.to/bro_force_af8dd8c3202a933/forge-a-zero-dependency-secret-scanner-and-the-stdlib-potholes-i-hit-building-it-49k6</link>
      <guid>https://dev.to/bro_force_af8dd8c3202a933/forge-a-zero-dependency-secret-scanner-and-the-stdlib-potholes-i-hit-building-it-49k6</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built &lt;code&gt;forge&lt;/code&gt; for raptors.dev's Zero Dependency Hackathon&lt;br&gt;
(Track F) — a single-binary secret scanner that ranks its own findings by&lt;br&gt;
confidence instead of just flagging them, on top of a hand-rolled&lt;br&gt;
log-structured store. Nothing outside Python's standard library. This is&lt;br&gt;
the story of a 327-false-positive run I almost buried, a build hash that&lt;br&gt;
changed on a machine I hadn't touched, and a benchmark number I genuinely&lt;br&gt;
did not want to put in the README — and put in anyway.&lt;/p&gt;

&lt;p&gt;For raptors.dev's Zero Dependency Hackathon (Track F, Open/Wildcard), I&lt;br&gt;
built &lt;strong&gt;Forge&lt;/strong&gt;: one &lt;code&gt;forge&lt;/code&gt; binary that does secret scanning, file&lt;br&gt;
search, dedup, repo stats, a terminal dashboard, run-to-run diffing, a&lt;br&gt;
risk timeline, and per-finding confidence explanations. Underneath all&lt;br&gt;
of it sits a hand-rolled log-structured key-value store. Nothing outside&lt;br&gt;
Python's standard library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5,502 lines in &lt;code&gt;src/forge/&lt;/code&gt;. 313 tests. 0 runtime dependencies. ~77 KB&lt;br&gt;
single-file build.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm not going to walk the whole feature list — the README does that.&lt;br&gt;
This is the honest version: what I actually rebuilt, a real bug that&lt;br&gt;
testing against live repos actually caught, where the standard library&lt;br&gt;
fought back, which real package I think I made pointless, and the one&lt;br&gt;
build bug that cost me an entire afternoon and taught me more than&lt;br&gt;
anything else in the project.&lt;/p&gt;
&lt;h2&gt;
  
  
  The number I didn't want to publish
&lt;/h2&gt;

&lt;p&gt;Every one of the numbers above makes Forge look good, so here's the one&lt;br&gt;
that doesn't. I benchmarked &lt;code&gt;forge scan&lt;/code&gt; against &lt;code&gt;detect-secrets&lt;/code&gt; on two&lt;br&gt;
targets — a small tree (42 files, 291 KB) and a large one (480 files,&lt;br&gt;
5.1 MB) — 7 timed runs each, cold scans, no result cache, matching&lt;br&gt;
&lt;code&gt;detect-secrets&lt;/code&gt;' no-cache behavior:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Forge&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;detect-secrets&lt;/code&gt; 1.5.0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Startup baseline (&lt;code&gt;--version&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.52s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.14s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small tree (42 files, 291 KB)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.79s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.61s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large tree (480 files, 5.1 MB)&lt;/td&gt;
&lt;td&gt;4.66s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.81s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan-only throughput, large tree&lt;/td&gt;
&lt;td&gt;~1.3 MB/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~7 MB/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Forge starts twice as fast and wins the small-tree case, because&lt;br&gt;
&lt;code&gt;detect-secrets&lt;/code&gt; pays import cost for &lt;code&gt;pyyaml&lt;/code&gt;, &lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;urllib3&lt;/code&gt;,&lt;br&gt;
and its full plugin registry before it scans a single byte. On the large&lt;br&gt;
tree, that startup advantage stops mattering and Forge's &lt;strong&gt;scan loop&lt;br&gt;
runs 5–6× slower per byte&lt;/strong&gt;. Twelve regexes plus entropy tokenization on&lt;br&gt;
every line, no real thread parallelism because of the GIL, versus a&lt;br&gt;
library that's had years to get its throughput right. I checked whether&lt;br&gt;
the confidence scorer was the cause before writing this — it's under 4%&lt;br&gt;
of scan time, so ranking isn't the tax. Raw regex throughput is.&lt;/p&gt;

&lt;p&gt;I don't have a fix for this that doesn't compromise something else I&lt;br&gt;
actually care about (zero deps, zero install, ranked output), so instead&lt;br&gt;
of hiding it in a footnote I put it in the README next to the win. The&lt;br&gt;
trade is real: Forge gives up large-tree throughput for zero&lt;br&gt;
installation and confidence-ranked findings; &lt;code&gt;detect-secrets&lt;/code&gt; gives up&lt;br&gt;
both of those for raw speed. Different tools optimizing for different&lt;br&gt;
things, and a judge should get to see the actual trade instead of a&lt;br&gt;
cherry-picked number.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture at a glance
&lt;/h2&gt;

&lt;p&gt;One CLI entrypoint, ten subcommands, everything fanning down into a&lt;br&gt;
single storage layer. This is the actual internal import graph, not an&lt;br&gt;
idealized version of it — grouped into three layers so the shape reads&lt;br&gt;
at a glance:&lt;br&gt;
&lt;/p&gt;

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

    subgraph L2["Feature modules"]
        Scanner["scanner.py&amp;lt;br/&amp;gt;12 regexes + entropy"]
        Dedup["dedup.py"]
        Stats["stats.py&amp;lt;br/&amp;gt;hand-parsed .git objects"]
        Dashboard["dashboard.py&amp;lt;br/&amp;gt;curses / ANSI fallback"]
        Watcher["watcher.py&amp;lt;br/&amp;gt;polling watch"]
        Baseline["baseline.py&amp;lt;br/&amp;gt;new-vs-existing gate"]
        Sarif["sarif.py&amp;lt;br/&amp;gt;SARIF 2.1.0"]
        History["history.py&amp;lt;br/&amp;gt;diff + risk timeline"]
    end

    subgraph L2b["Scoring"]
        Confidence["confidence.py&amp;lt;br/&amp;gt;naive-Bayes log-odds scorer"]
        Entropy["entropy.py"]
    end

    subgraph L3["Persistence"]
        Cache["cache.py"]
        Storage[("storage.py — LogStore&amp;lt;br/&amp;gt;CRC32 · crash recovery")]
    end

    CLI --&amp;gt; Scanner &amp;amp; Dedup &amp;amp; Stats &amp;amp; Dashboard &amp;amp; Watcher &amp;amp; Baseline &amp;amp; Sarif &amp;amp; History

    Scanner --&amp;gt; Cache
    Scanner --&amp;gt; Confidence
    Dashboard -.-&amp;gt;|reuses| Scanner
    Watcher -.-&amp;gt;|reuses| Scanner
    Confidence --&amp;gt; Entropy

    Cache --&amp;gt; Storage
    History --&amp;gt; Storage

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



&lt;p&gt;Solid arrows are "needs this to function"; dashed arrows labeled &lt;em&gt;reuses&lt;/em&gt;&lt;br&gt;
are "calls this subcommand's logic directly" (&lt;code&gt;dashboard&lt;/code&gt; and &lt;code&gt;watcher&lt;/code&gt;&lt;br&gt;
both call into the scanner rather than re-implementing scan logic). Two&lt;br&gt;
things worth noticing about the &lt;em&gt;shape&lt;/em&gt;: everything that needs to&lt;br&gt;
persist between runs — the cache and the risk/diff history — funnels&lt;br&gt;
through one storage engine, so &lt;code&gt;scan&lt;/code&gt;, &lt;code&gt;dedup&lt;/code&gt;, &lt;code&gt;stats&lt;/code&gt;, and &lt;code&gt;diff&lt;/code&gt; all&lt;br&gt;
get crash-safe persistence for free instead of each subcommand rolling&lt;br&gt;
its own file format. And confidence scoring gets its own layer, not&lt;br&gt;
tucked inside the scanner as an afterthought — ranking isn't&lt;br&gt;
post-processing, it's a first-class step between "regex matched" and&lt;br&gt;
"finding reported," which is why &lt;code&gt;forge explain&lt;/code&gt; can reconcile its&lt;br&gt;
per-term breakdown back to the exact score &lt;code&gt;forge scan&lt;/code&gt; printed.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I reimplemented
&lt;/h2&gt;

&lt;p&gt;The obvious piece is the scanner itself: twelve regexes for AWS keys,&lt;br&gt;
GitHub tokens, Stripe secret keys, GitLab PATs, JWTs, PEM blocks, plus a&lt;br&gt;
Shannon-entropy fallback for anything that doesn't match a known shape.&lt;br&gt;
That part is table stakes for this hackathon category — most stdlib&lt;br&gt;
secret scanners stop here.&lt;/p&gt;

&lt;p&gt;The part I actually care about is &lt;code&gt;confidence.py&lt;/code&gt;: every finding gets&lt;br&gt;
scored &lt;strong&gt;0.00–1.00&lt;/strong&gt; by a hand-tuned naive-Bayes classifier evaluated in&lt;br&gt;
log-odds space — the exact math behind a spam filter, done by hand&lt;br&gt;
instead of &lt;code&gt;scikit-learn.fit()&lt;/code&gt;:&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="n"&gt;log_odds&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;logit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prior_for_this_rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;entropy_term&lt;/span&gt;
          &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;character_diversity_term&lt;/span&gt;
          &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ordered_sequence_term&lt;/span&gt;
          &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;match_length_term&lt;/span&gt;
          &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;comment_marker_term&lt;/span&gt;
          &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;file_type_term&lt;/span&gt;
&lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_odds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seven signal families feed it — pattern specificity, Shannon entropy,&lt;br&gt;
character-class diversity, monotonic-run detection (catches a&lt;br&gt;
hand-written &lt;code&gt;ABCDEF...&lt;/code&gt; charset that would otherwise score as&lt;br&gt;
high-entropy &lt;em&gt;and&lt;/em&gt; high-diversity), match length against known&lt;br&gt;
fixed-width formats, inline &lt;code&gt;# nosec&lt;/code&gt;/&lt;code&gt;# noqa&lt;/code&gt; markers, and file-path&lt;br&gt;
context. A hard override kills obvious placeholders like &lt;code&gt;changeme&lt;/code&gt; or&lt;br&gt;
&lt;code&gt;&amp;lt;token&amp;gt;&lt;/code&gt; outright. Every weight is documented inline where it's added,&lt;br&gt;
specifically so a judge — or me, six months from now — can read the&lt;br&gt;
score instead of trusting it.&lt;/p&gt;

&lt;p&gt;Underneath that, &lt;code&gt;storage.py&lt;/code&gt; is an append-only, CRC32-checksummed,&lt;br&gt;
log-structured key-value store with crash recovery, backing the shared&lt;br&gt;
cache/history layer for every subcommand that needs to remember a&lt;br&gt;
previous run.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing it on real repos found a real bug, not a hypothetical one
&lt;/h2&gt;

&lt;p&gt;Talk is cheap for a secret scanner, so I shallow-cloned three public&lt;br&gt;
repos I didn't pick for a favorable result — &lt;code&gt;pallets/click&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;psf/requests&lt;/code&gt;, &lt;code&gt;expressjs/express&lt;/code&gt;, 596 files, ~10.4 MB — and read every&lt;br&gt;
single finding by hand. None of the three has a real committed secret,&lt;br&gt;
so the right answer for all three is "nothing," and the interesting&lt;br&gt;
question is how much noise Forge generates getting there.&lt;/p&gt;

&lt;p&gt;The first run of that validation found &lt;strong&gt;327 findings in &lt;code&gt;requests&lt;/code&gt;&lt;br&gt;
alone.&lt;/strong&gt; Not 2 — 327. 325 of them were base64 chunks inside one file:&lt;br&gt;
&lt;code&gt;ext/requests-logo.ai&lt;/code&gt;, a PDF-wrapped Adobe Illustrator asset. My binary&lt;br&gt;
check only sniffed the first 4 KB of a file for a NUL byte, and &lt;code&gt;.ai&lt;/code&gt;&lt;br&gt;
files carry a long plain-text header (PDF structure plus XMP metadata)&lt;br&gt;
that outruns 4 KB before the binary payload starts — so Forge read the&lt;br&gt;
file as text and dutifully entropy-flagged every base64 run in it.&lt;/p&gt;

&lt;p&gt;Fix: run the binary check over the full file content Forge already has&lt;br&gt;
in memory, not just a 4 KB prefix, so a NUL byte anywhere marks the file&lt;br&gt;
binary. After that, &lt;code&gt;requests-logo.ai&lt;/code&gt; contributes &lt;strong&gt;zero&lt;/strong&gt; findings, and&lt;br&gt;
the three-repo total drops to 14 — down to genuine low/medium noise (a&lt;br&gt;
hash in a doc, an &lt;code&gt;ABCD…wxyz&lt;/code&gt; alphabet in a docstring, a dozen&lt;br&gt;
ETag/hash literals in &lt;code&gt;express&lt;/code&gt;'s own test fixtures). Every one of those&lt;br&gt;
12 &lt;code&gt;express&lt;/code&gt; findings landed in confidence's medium band (0.41–0.49);&lt;br&gt;
&lt;strong&gt;zero reached high-confidence&lt;/strong&gt; — which is the actual claim &lt;code&gt;--allowlist&lt;/code&gt;&lt;br&gt;
and the pre-commit hook lean on: the ranking keeps ordinary noise out of&lt;br&gt;
the tier that would actually block a commit.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;repo&lt;/th&gt;
&lt;th&gt;files&lt;/th&gt;
&lt;th&gt;findings (after fix)&lt;/th&gt;
&lt;th&gt;high-confidence&lt;/th&gt;
&lt;th&gt;true positives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;click&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;195&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;159&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;express&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;242&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I'm including the 327-finding number, not just the fixed 14, because a&lt;br&gt;
write-up that only shows the clean result after the fact isn't proof of&lt;br&gt;
anything — the bug is the evidence the validation actually happened.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where the standard library actually made me suffer
&lt;/h2&gt;

&lt;p&gt;Two places, honestly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;curses&lt;/code&gt; doesn't exist on Windows.&lt;/strong&gt; Not "harder to use" — it's simply&lt;br&gt;
not part of the CPython standard distribution there. &lt;code&gt;forge dashboard&lt;/code&gt;&lt;br&gt;
needed a live-updating terminal view, and the natural stdlib answer&lt;br&gt;
(&lt;code&gt;curses&lt;/code&gt;) evaporates the moment a judge runs this on Windows, which —&lt;br&gt;
given this hackathon's toolchain — a lot of them will. The fallback is a&lt;br&gt;
plain-text view that redraws on an interval using raw ANSI escape codes.&lt;br&gt;
It works, but it's not the same widget, and I say so directly in the&lt;br&gt;
README instead of pretending the two code paths are equivalent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Determinism across platforms is not free.&lt;/strong&gt; &lt;code&gt;os.walk&lt;/code&gt; order is sorted&lt;br&gt;
on NTFS and effectively hash-order on ext4. &lt;code&gt;forge stats --json&lt;/code&gt; returned&lt;br&gt;
its &lt;code&gt;files_by_ext&lt;/code&gt;/&lt;code&gt;bytes_by_ext&lt;/code&gt;/&lt;code&gt;lines_by_ext&lt;/code&gt; maps straight from that&lt;br&gt;
walk, so the same repo produced byte-different JSON depending on the&lt;br&gt;
filesystem underneath it — invisible in the human-readable table (which&lt;br&gt;
was already sorted by count for display), silently wrong in the machine&lt;br&gt;
contract judges would actually diff. Fix was mechanical once I found it&lt;br&gt;
— sort the maps at construction — but "the stdlib gives you an iteration&lt;br&gt;
order, not a &lt;em&gt;guarantee&lt;/em&gt;" is a lesson that doesn't show up until you&lt;br&gt;
actually run the same code on two OSes.&lt;/p&gt;
&lt;h2&gt;
  
  
  The package I think I made unnecessary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;detect-secrets&lt;/code&gt; (Yelp) — 1–2M downloads/month by PyPI's own tracker,&lt;br&gt;
and the closest real category match to what &lt;code&gt;forge scan&lt;/code&gt; does: regex&lt;br&gt;
plus entropy heuristics, a baseline file, a CI/pre-commit gate. I&lt;br&gt;
installed &lt;code&gt;detect-secrets==1.5.0&lt;/code&gt; into a throwaway venv and measured&lt;br&gt;
instead of guessing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Forge&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;detect-secrets&lt;/code&gt; 1.5.0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime deps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2 direct, 6 total installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installed footprint&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0 bytes&lt;/strong&gt; (whole 10-subcommand CLI is ~77 KB)&lt;/td&gt;
&lt;td&gt;~4.3 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ranks findings by confidence&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No — flags only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is the actual pitch, not the dependency count.&lt;br&gt;
&lt;code&gt;detect-secrets&lt;/code&gt; treats every regex hit as equally worth a human's time.&lt;br&gt;
Forge ranks them, so a bare AWS key ID sitting in &lt;code&gt;config/production.env&lt;/code&gt;&lt;br&gt;
sorts to the top and a high-entropy string on a line ending in &lt;code&gt;# noqa&lt;/code&gt;&lt;br&gt;
inside &lt;code&gt;tests/fixtures/&lt;/code&gt; sorts to the bottom — the same signal every&lt;br&gt;
commercial scanner (GitGuardian, GitHub secret scanning) sells as a&lt;br&gt;
headline feature, done here in ~170 lines of &lt;code&gt;math&lt;/code&gt; and &lt;code&gt;re&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'll say the honest gap too: &lt;code&gt;detect-secrets&lt;/code&gt; has a real plugin&lt;br&gt;
architecture and an interactive &lt;code&gt;audit&lt;/code&gt; workflow for annotating false&lt;br&gt;
positives over time that persists across a team. Forge's flat&lt;br&gt;
&lt;code&gt;--allowlist&lt;/code&gt; file is the lighter-weight version of that, not a clone.&lt;/p&gt;
&lt;h2&gt;
  
  
  The edge case that ate an afternoon
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reproducible builds and &lt;code&gt;core.autocrlf&lt;/code&gt; do not get along.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Forge claims a byte-reproducible build — rebuild &lt;code&gt;dist/forge.pyz&lt;/code&gt; from&lt;br&gt;
source and it should hash identically every time, so a judge can verify&lt;br&gt;
the exact artifact they're running matches the repo. On my machine, it&lt;br&gt;
did. Then I checked out the repo fresh on a different Windows setup with&lt;br&gt;
&lt;code&gt;core.autocrlf=true&lt;/code&gt; (the Windows default) and the hash &lt;em&gt;changed&lt;/em&gt;. Same&lt;br&gt;
source, same commit, different SHA-256.&lt;/p&gt;

&lt;p&gt;Two separate bugs stacked on top of each other:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;scripts/build.py&lt;/code&gt; was reading source bytes verbatim into the zipapp.
A Windows autocrlf clone silently converts every &lt;code&gt;\n&lt;/code&gt; in the checkout
to &lt;code&gt;\r\n&lt;/code&gt; on the way out of git — so the "same" source file was
actually different bytes depending on which machine cloned it, before
my build script ever touched it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scripts/bundle_single_file.py&lt;/code&gt; was writing its output with
&lt;code&gt;write_text()&lt;/code&gt;, which on Windows happily re-introduces platform line
endings on the way &lt;em&gt;out&lt;/em&gt;, even if the input was clean.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither bug is visible from reading either script in isolation — both&lt;br&gt;
looked correct. It only showed up as "the hash doesn't match" after a&lt;br&gt;
clean clone on a machine I hadn't built on before, which is exactly the&lt;br&gt;
scenario a judge running the reproducibility check would hit.&lt;/p&gt;

&lt;p&gt;The fix, once diagnosed: normalize all source to &lt;code&gt;\n&lt;/code&gt; before archiving,&lt;br&gt;
switch the bundler to &lt;code&gt;write_bytes()&lt;/code&gt; with an LF-pinned bootstrap, and&lt;br&gt;
add a &lt;code&gt;.gitattributes&lt;/code&gt; pinning &lt;code&gt;* text=auto eol=lf&lt;/code&gt; (with &lt;code&gt;*.pyz&lt;/code&gt;&lt;br&gt;
declared binary so git doesn't touch the archive itself). I also found&lt;br&gt;
&lt;code&gt;zipfile&lt;/code&gt; stamps each entry's &lt;code&gt;create_system&lt;/code&gt; byte from the &lt;em&gt;build OS&lt;/em&gt;&lt;br&gt;
(0 on Windows, 3 elsewhere) — so even with identical &lt;code&gt;zlib&lt;/code&gt; output, a&lt;br&gt;
Windows-built and Linux-built &lt;code&gt;.pyz&lt;/code&gt; still differed on that one metadata&lt;br&gt;
byte. Pinned that too.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tests/test_build.py&lt;/code&gt; now has explicit regression tests —&lt;br&gt;
&lt;code&gt;test_pyz_hash_is_independent_of_source_line_endings&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;test_single_file_hash_is_independent_of_source_line_endings&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;test_committed_source_and_artifacts_are_lf_only&lt;/code&gt; — so this doesn't come&lt;br&gt;
back quietly. And &lt;code&gt;judge_mode.py&lt;/code&gt; re-derives both artifact hashes as one&lt;br&gt;
of its 13 checks, so "reproducible" is something a judge runs in seconds&lt;br&gt;
instead of taking on faith.&lt;/p&gt;

&lt;p&gt;The lesson wasn't really about git or zipfile. It was that "reproducible&lt;br&gt;
build" is a claim about &lt;em&gt;bytes&lt;/em&gt;, and bytes have opinions about line&lt;br&gt;
endings and OS metadata that never show up until someone else's&lt;br&gt;
environment disagrees with yours.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;Knowing what I know now, I'd build this exact reproducible-build check&lt;br&gt;
&lt;em&gt;first&lt;/em&gt;, not near the end. &lt;code&gt;judge_mode.py&lt;/code&gt;'s hash verification came&lt;br&gt;
after the CRLF bug above had already shipped once — which meant the bug&lt;br&gt;
reached a committed artifact before anything caught it. If the&lt;br&gt;
byte-comparison check had existed from day one, that whole category of&lt;br&gt;
bug gets caught in CI on the first Windows run, not discovered by&lt;br&gt;
accident on a second machine days later. The fix itself — normalize line&lt;br&gt;
endings before archiving — took twenty minutes once diagnosed.&lt;br&gt;
Diagnosing it took the afternoon. Building the verification first would&lt;br&gt;
have collapsed those into the same twenty minutes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where it landed
&lt;/h2&gt;

&lt;p&gt;313 tests pass. &lt;code&gt;dist/forge.pyz&lt;/code&gt; and &lt;code&gt;dist/forge_single.py&lt;/code&gt; are&lt;br&gt;
committed, byte-reproducible, and independently rebuildable from&lt;br&gt;
&lt;code&gt;src/forge/&lt;/code&gt;. &lt;code&gt;deps-proof.txt&lt;/code&gt; statically checks every import against&lt;br&gt;
the standard library, then re-checks it under an isolated interpreter&lt;br&gt;
with no site-packages on &lt;code&gt;sys.path&lt;/code&gt; at all. &lt;code&gt;STDLIB.md&lt;/code&gt; documents 21&lt;br&gt;
substitutions, each backed by an actual import in the codebase, not a&lt;br&gt;
hypothetical.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;runs all of the above — plus the confidence-score reconciliation, the&lt;br&gt;
risk-timeline formula, the SARIF 2.1.0 output, and a deliberate&lt;br&gt;
storage-corruption test — as one command, in under 15 seconds, and exits&lt;br&gt;
non-zero if any claim in this post doesn't hold up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forge is my submission to &lt;a href="https://dev.to/raptorsdev"&gt;Hackathon Raptors&lt;/a&gt;'&lt;br&gt;
Zero Dependency Hackathon — Track F (Open/Wildcard).&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Anyone with an AI coding agent can produce a scanner that finds AWS keys&lt;br&gt;
with a regex — that part isn't the hard 20%. The hard part was building&lt;br&gt;
enough ways to catch myself being wrong: a &lt;code&gt;judge_mode.py&lt;/code&gt; that&lt;br&gt;
re-derives every claim in this post instead of asking a judge to trust&lt;br&gt;
it, a real-world validation that kept the 327-finding run instead of&lt;br&gt;
only publishing the fixed one, and a benchmark table honest enough to&lt;br&gt;
put a 5–6× loss next to a 2× win. The scanner is the artifact. The&lt;br&gt;
willingness to publish the run that didn't flatter it is the actual&lt;br&gt;
submission.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://github.com/broforce6909-cmd/forge" rel="noopener noreferrer"&gt;GitHub Repository — broforce6909-cmd/forge&lt;/a&gt;&lt;/p&gt;

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

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

</description>
      <category>python</category>
      <category>opensource</category>
      <category>security</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
