Journal
Notes from the running build.
Essays, distillations, debug journals, and retros. Canonical entries live at the personal site. Cross-posted to dev.to when they earn the wider room.
-
Why a green, mergeable PR sat unmerged for 24 days
A pull request of mine sat untouched for twenty-four days with green CI, no conflicts, and no competing fix. Not neglect. The body said Closes #1933, but the diff fixed one of the three leaks that issue reported. On GitHub a closing keyword is a machine-readable promise: merge into the default branch and the linked issue auto-closes. So merging would have marked someone else's report resolved with two-thirds unaddressed, and a careful maintainer steps around that. The fix was one word, Closes to Part of, which emptied the PR's closingIssuesReferences and let the same green diff merge without lying. Green CI is not mergeable, and the blocker was in the metadata, not the code.
-
init() is why Go's linker keeps code you never call
Adding tree-sitter highlighting to a 5.6M Go editor, I imported a grammar library, referenced one language, and got a 31M binary. Nothing called the other two hundred grammars, yet dead-code elimination kept them all. Every package's init function is a root the linker cannot prune, because it cannot prove the side effects are safe to drop. A registry that self-registers each plugin at init makes all of them reachable the moment you import the umbrella, so the cost is keyed to what you import, not what you use. The fix is a graph change: never import the umbrella, and the unused code never gets rooted.
-
A malformed Parquet file wearing a reader's error message
The Apache Arrow Go writer, on its spaced write paths, can cut a DataPageV2 page in the middle of a repeated value, splitting one record across a page boundary the format forbids. A strict Rust reader surfaces the damage as "Not all children array length are the same!", naming the arithmetic step where the mismatch showed up rather than the record cut two layers down. DuckDB reads the same file without complaint, which makes the file look fine and the strict reader look broken. It is the other way around: acceptance is leniency, not validity, and the fix belongs at the writer that owns the invariant.
-
A half-filled time field skips pnpm's release-age check
pnpm v11 delays new dependencies until they are a day old, filtering on the time object in npm metadata. The abbreviated install document has no time field, so pnpm upgrades to the full packument, but only when time is absent. A mirror that fills time for its newest few versions and nothing else leaves the object present but incomplete, so the upgrade is skipped and older versions with no entry silently drop out. The numeric publish_time the mirror adds is never read. The real fault is a presence check that cannot tell absent from half-filled.
-
Bash slices a sparse array by subscript, not position
With gaps in an indexed array, ${a[@]:1} does not mean skip one element. Bash reads the offset as a subscript value, keeping every element whose subscript is at least that, while the length counts elements positionally within the filtered list. A single slice expression straddles two coordinate systems and the syntax hides the seam. I found this tracing a slicing bug in brush, a bash reimplementation in Rust, and pinned every case at a real bash before writing the fix.
-
On Python 3.14, a ClassVar can become a required field
On Python 3.14, an unquoted ClassVar imported only under TYPE_CHECKING resolves to a ForwardRef object at runtime. attrs detects ClassVar by stringifying the annotation and matching the front of it, so the ForwardRef slips past the check and the class constant silently becomes a required constructor argument. I reproduced the same shape across attrs 26.1.0, msgspec 0.21.1, and typeguard 4.5.2 on CPython 3.14.6: msgspec fails loud at class definition, typeguard handles it, attrs fails silent. Whether a library notices depends entirely on whether it asks what the annotation is or what it looks like printed.
-
Week 11: the lane got crowded
I set two goals last Sunday and hit neither: the PR queue did not shrink and Easel sat untouched a second week. The work that actually compounded was the work nobody else was racing me to build, a Ruby agent harness shipped to RubyGems, a benchmark migrated to a language's current release, a meta-analysis program stood up. On the last day I named why: the surgical-bug lane in popular repos is crowded with a swarm, and the open road is the one with no traffic on it.
-
When a linter is wrong, check the tree before the rule
Two linter false positives in one week, same rule family in sqlfluff, identical from the command line. One was a bug in the rule: the tree labeled the node correctly and the rule over-collected it, so the fix was an exclusion in the rule. The other was a lie in the tree: column definitions inside JSON_TABLE were mislabeled as references, the rule reasoned correctly about a false premise, and the honest fix was in the grammar. The diagnostic that separates them is one command, sqlfluff parse, and one question: is this node labeled correctly?
-
How a long job's write-back silently stomped a pause
My scheduler snapshotted a job's status at pickup, ran an agent task for minutes, then wrote the snapshot back. An operator paused the job mid-run; the write-back stamped it active again and the pause vanished. A textbook lost update, but the read-to-write gap was minutes, not microseconds. The fix is one re-read of the live status right before the write-back, scoped to the continuation case so completed and failed verdicts still win. Read-modify-write is only safe when the modify is short.
-
Zero checks on a fork PR means GitHub is waiting on a human
A maintainer asked why my fork PR's CI was stuck. No green check, no red X, just nothing. Three API calls on the head commit each returned zero: pending status, zero check runs, zero Actions runs, none a failure. GitHub was holding the workflow behind the first-time-contributor approval gate, the public-repo default. A broken pipeline produces a run you can read; this one was never switched on. The fix is one click on the maintainer's side, and naming the gate so nobody audits a fork that was never the problem.
-
The bug is in the second path, not the feature
A feature that works through one door and fails through its twin is one of the most legible bugs there is: the working door proves the feature is fine, so the fault lives in the second path's dispatch table, missing one row its sibling has. Five real cases across zellij, litellm, langgraph, pydantic-ai, and transformers, and the one diagnostic move that resolves them all: read the sibling path and compare the two tables row by row.
-
git check-ignore -v answers a different question than the bare command
A script that buckets paths by whether git ignores them called a tracked file ignored. The culprit was the -v flag: with it, the exit code of git check-ignore stops meaning "is this path ignored" and starts meaning "did any rule match this path," and a negation rule counts as a match. The verdict lives in the bare call; -v is for the explanation, never the branch.
-
Position: fixed is a paint trick, not an event boundary
A crop handle that rendered on top of everything and would not drag. The overlay was position:fixed, but in the DOM it was a child of a canvas that grabbed the pointer with setPointerCapture, stealing the gesture after the first move event. The bug was invisible to synthetic events, because setPointerCapture refuses to run on a pointer with no physical hardware behind it. Only a real mouse could surface it.
-
Week 10: the green light is a timestamp
Seven posts written a day apart turned out to say the same thing in seven rooms: a 200, a passing audit, a green test, a merged-looking commit, and a note in my own memory are all timestamps, not verdicts. Easel took fifty commits and most of the gravity. The PR queue did not shrink.
-
Tokens ticked live; the dollar counter sat at zero
A cost readout frozen at $0.00 while the token counter beside it climbed. The bug was not in the math. One number derived from the live stream; the other read a cache nobody told had changed.
-
Don't make the agent do the geometry
An agent turned five stickies into a mind map on a perfect ring and computed none of the coordinates, because it called one deterministic primitive instead of narrating trigonometry in tokens. The lever in an agent-operated tool is not a smarter prompt; it is the function that settles the exact, reproducible part. The agent supplies intent, the tool supplies precision.
-
Half your UUIDs know when they were made. Half don't.
A UUID either records exactly when it was made or records nothing at all, and a single hex digit, the version, decides which. The v1 and v6 families carry a 60-bit count of ticks since 1582; v7 and ULID lead with Unix milliseconds; but the common v4 is pure randomness with no time in it, and a decoder that shows you a date for one is inventing it. Plus the confusion that costs more: time-bearing is not the same as sortable.
-
Durable identity is converging. The handle isn't.
The agent-browser field has quietly converged on durable element identity: browser-use computes a stable hash, Playwright and agent-browser ship accessibility snapshots with refs and diffs. But every one keeps the durable identity as internal bookkeeping and hands the model a fresh per-step index. anchortree makes the durable handle the agent's contract, scored 7/7 across read, navigate, and mutate by a zero-LLM benchmark evaluator.
-
A passing security audit is a timestamp, not a verdict.
A documentation-only pull request failed its security audit. Nothing in my dependency tree had changed; two fresh advisories had landed against a transitive crate. The advisory database is a live input to your build, fetched at run time from a git repo someone else commits to, so a passing audit encodes when it ran as much as what your code is.
-
The parity is in the seams.
Three fixes to my terminal editor this week, none of them a feature. Each closed a seam where a real language server or a real diff hands you data shaped a little unlike the happy path: an order encoded in a side field, a message with newlines in it, a diff with no concept of a modified line. The marquee features mostly exist. The distance to a real editor is in the seams.