Builtpublic

Silent failures

Almost every bug this project produced returned success and did nothing.

0 errors

Lining up the bugs collected while bolting features on, almost all of them had the same shape.

A bug ORDINARY THIS PROJECT Exception throw Stack trace it's in the log You notice then you fix it Returns success ok: true Empty result zero errors Logs look fine nobody knows one hour weeks
Failures that throw are easy. This program's failures returned success and did nothing.

Returning success while doing nothing

Someone running a fork sent three issues with diagnoses and patches attached, and all three were exactly this.

Decimal places — Slack’s history API, given a timestamp with more than six decimals, doesn’t error. It returns ok: true and an empty list. Python’s time.time() usually gives seven. So reading channel context ran on empty history for weeks. Auditing every call site, this was the only place passing a float through — and that one place silently disabled an entire feature.

Deny beats allow — the guest fence blanket-denies other drives, so if the work folder lives on one of them, every allowlisted path dies. The only message you get is “permission denied,” which reads like a broken allowlist rather than a broken fence.

Aliases that save and never fire — the name-collision check hardcoded the shipped command names, so plugin commands were invisible to it. Saving succeeds. Calling it does nothing.

Read the structure, not the string

This one started as a broken command and ended as a new feature.

A human typing !usage into the Slack composer worked fine. But when a session on another machine sent it through the connector, every command missed. Slack appends an attribution to the end of the message text — on the same line, no newline.

The symptom split two ways. Anchored commands failed to match at all and leaked through to the model (which duly replied “there’s no such command”), while looser ones matched with contaminated arguments.

My first move was to strip that phrase. Wrong approach — the attribution is translated into the workspace language. Korean, English, Japanese all arrive differently. Matching on words breaks the moment someone runs it in another locale.

The answer wasn’t in the string but in the structure Slack already handed over. The attribution isn’t mixed into the body; it’s a separate trailing block, and the human’s own text stays intact next to it. And a human physically cannot produce that block from the composer — which means there are no false positives, by construction.

Here’s the feature that came out of the fix. A session on another machine posts to Slack as me, and the Loki on this PC runs it and replies in the thread. Because it’s a human account rather than a bot, no second Slack app is required.

A liveness check can kill the thing it checks

While building the watchdog, I wrote the worker-alive check as os.kill(pid, 0). On Unix that’s the idiom — signal 0 sends nothing and only asks whether the process exists.

Windows Python implements that as process termination. Signal 0 included. The liveness check kills the worker.

Replaced with a read-only query. There was one more trap in the same family: a restart once raced the five-minute watchdog and left three workers attached. Three sockets on one token means events get split. But the health check reads a single recorded pid, so it reports everything normal.

Detection now goes by process lineage, not count. Counting can’t work here: the launch method legitimately produces a parent and a child, so neither “two is wrong” nor “it must be one” holds.

I gave up on one piece of automatic detection

I tried twice to automatically detect “this conversation is spinning in place” and wire it into the nudges. Both attempts failed. They measured lexical novelty.

The reason for rejecting it wasn’t a tuning failure — it was the measurement itself. The novelty range of a conversation that’s genuinely progressing overlaps with the range of one restating the same deadlock in new words. No threshold separates them.

The reason is structural. “A new question on the same topic” and “the same deadlock, rephrased” use the same words. Tuning a threshold until three examples pass is overfitting, not evidence.

So I went instead to things that need to understand nothing — a per-hour request cap, a cost ceiling, an explicit command to end a conversation. The cost figure was already arriving in the response and being thrown away.

The tests were editing live state

Burned twice by the same class. Each module binds its state path at import time, so unless the test setup blocks them one by one, live files get edited.

The actual incident: an adapter test flowed a channel message through, that wrote to the live state file, and a warning I had just built silently switched off in production. Every test passed.

Now all seventeen state paths get redirected wholesale. And adding a new state file means adding it to that list — this is the kind of thing the person who built it forgets, not the next person.