Tagged node
24 write-ups.
Giving an Open-Weight Language Model the Vote in Among Us Meetings
I let gpt-oss:20b play every seat in my Among Us meetings and compared it with the small trained networks that otherwise vote. It never did better: alone, the crew won 2 of 16 games against 52.0%, and with the network's guesses in its prompt it matched it once and fell short four times.
Rebuilding the Among Us Floor Plan From the Game's Own Collision Data
My Among Us simulator let players stand on 3,314 tiles of floor. The 2018 game's own colliders allow 1,894, so the floor was 1.75 times too big, and 3.06 times in O2. In a seeded 40-game test the fix changed the crew's wins by 2, within noise; an earlier test saw 10 points.
Asking Trained Among Us Bots Why They Moved
My Among Us crew bots explain each move by splitting the decision score into one part per input, exact to about 1e-14 over 155,209 decisions. The captions built from it were still wrong: 45 of 61 moves captioned "fixing the reactor" went farther from the reactor.
Training Among Us Bots: The Crew That Stopped Fixing the Reactor
Ten thousand games into a self-play run, my Among Us impostors found that sabotaging the reactor beat the crew. The crew now weighted its next task 4.1 times as much as the fix, and fixed 0 of 7 sabotages. Paying for each fix flipped that to 7 times toward the fix, and 30 of 34.
Giving Evolved Monopoly Players a Memory of Past Trades
Evolved Monopoly players with a memory of past trades won 56.1% of two-against-two games against the same setup without one. A third population that barely used its one history-based input won 55.8% against the same opponents, so that margin does not show that memory helped.
Why NEAT Stopped Forming Species When the Starting Genome Grew
The same NEAT code that split an XOR population into 13 species in ten generations kept a Monopoly population in exactly one, because a 180-gene starting genome shrank every compatibility distance to a median of 0.368 against a threshold of 3. Counting only the genes added since the start moved that median to 6.379.
Hill Climbing, Backpropagation and NEAT on the Same XOR Problem
I expected backpropagation to learn XOR with the least work. At learning rate 0.5 it needed 9,268 to 11,124 network runs where a random-nudge hill climber needed 1,512 to 1,648. Only on a two-output full adder did it win, needing about a third of the hill climber's work.
Code Completion From Your Own Repository's Statistics
One number decides whether it helps: the recital rate, 13.5% to 72.9% across nine corpora. Above 60% recital the n-gram index beats a ten-line frequency table; at 13.5% it beats nothing, the harness prints the null, and one published row was wrong and is corrected in the README.
Finding Duplicate Functions by Executing Them, Not Reading Them
Two functions agreed on every rung of the input ladder and were not the same function; one character, ½, turned same into differs with a witness. The guard exists because agreement is cheap: constants, projections and copies agree with everything, and each mistake was made before it was guarded.
Measuring How Cost Scales by Counting Instead of Timing
Insertion sort on seed 17 at n=64 performs reads 3812 and writes 1848, the same integers in Python and JavaScript, because the generator, the iteration rule and the subscript rule are part of the contract. The same parity suite caught its own dependency formatting one number two ways above a million.
A Curve Fitter That Refuses to Answer
A tool that always produced a constant would be useless and would still pass every test that checks it produces one. 0.2.0 fixed a defect the suite had pinned: with error bars from scatter alone, a quantity measured exactly was reported as one that could not be determined.
A Check With a Zero Denominator Reports Clean
A JUnit file reading tests="50" skipped="50" is a green run of nothing wearing a total, so every parser here returns total and executed separately and the floor applies to executed. Thirteen mutations were applied to the source; the two that survived their first run were worth more than the eleven that were caught.
An Exit Code Cannot Say Whether Anything Happened
go test on a tree with no test files prints [no test files] and exits 0, measured rather than assumed. didrun wraps a command, demands evidence it did something, and returns four states instead of one number; the committed control asserts that 0 passed satisfying the expected pattern still scores as did-not-run.
Proving the Tree Came Back After Breaking It on Purpose
finally does not run on SIGTERM, and a restore that ran is not a restore that worked. The suite kills real children: a try/finally harness leaves the mutated file on disk, the guarded one comes back byte for byte, and SIGKILL defeats both, so the last check lives one process out.
Ten Packages, One Rule: A Check Must Be Able to Fail
Eight of the ten went up in one day, extracted from a year of measurement work in which the thing lying was usually the instrument, not the code. Each package encodes one way a green check can mean nothing, and each commits a named test that would report its own premise wrong.
An Open Redirect Guard That Rebuilt What It Rejected
isSafeReturnTo("//bad.example.com") returns false, and the fallback for that rejection handed the browser the same string back, rebuilt from the request path. 0.3.0 closed one open redirect and shipped a second one for eight more days; both had been there since the first publish in 2019.
What a Rate Limiter Reports, and What the Server Receives
My crawler's per-process limiter reported a perfect interval while the host received every request at once. Pointing the same server-side test at Bottleneck, the most-downloaded rate limiter on npm: six cluster workers configured for 2 requests/second delivered 14.04, and every limiter reported 495.8 ms spacing.
A PDF Exporter With No PDF Library
PDF wants JPEG bitstreams verbatim and zlib-wrapped deflate for everything else, and a browser already emits both. That makes the exporter 336 lines with no dependency, and moves the only place it can quietly break into the cross-reference table.
What a Language Needs Before It Can Compile Itself
Porting a 129-line JavaScript lexer to my own low-level language produced 355 lines. Almost none of the difference is verbosity: six specific absences account for it, one of them caused the port's only correctness bug, and one I filed as merely annoying turned out to be a correctness problem two stages later.
When a SQL Engine Records Column Types but Never Reads Them
sql-nodejs accepts `id INT` and stores every value as a string. `WHERE age=25` matches a row and `WHERE age=25.0` matches nothing, because the comparison is string equality between two fragments of the query text.
Choosing an Unreleased API Over the One Already There
I removed a dependency on an unreleased router method in two lines and watched it pass CI on three Node versions, then put it back, because a public method is better code than the private field I had swapped in. I priced that choice at a few weeks and never priced it again.
What Maintaining a Forked npm Package Actually Buys
I would have said "eighteen releases of upkeep". What npm audit can see is one digit in a dependency range: a caret below 1.0.0 walls off the published fix, so it reports "No fix available". What it could not see is the open redirect my fork shipped for seven years while auditing clean.
Reimplementing Enough of Kubernetes to Fool kubectl
Reimplementing enough of the Kubernetes API that the real kubectl can't tell the difference. The surprise wasn't the resource schemas; it was that kubectl delegates its own output formatting to the server, and that a Quantity sent as a plain string decodes as zero on the client rather than erroring.
Rate Limiting a Crawler Across Node Cluster Workers
A per-process rate limiter under Node's cluster module enforced a perfect five-second delay while the host on the other end received a hundred requests at once. Fixing it took an atomic claim, and two more bugs on the way.