Charity and Claude go to the Zoo

Eleven versions in a day

The first version was a hundred lines and it was wrong in four ways I didn’t find until it ran.

The idea was simple enough. There’s a subreddit where people post when they’re about to lose their housing. A few days ago I answered one — an eighteen-year-old landing in a city in eastern Massachusetts with nowhere to go — and the thing that made my answer useful wasn’t compassion, it was knowing that Massachusetts routes homeless services by region, so an answer aimed at the wrong half of the state is worse than no answer at all. It looks helpful. It wastes a phone call somebody only has one of.

So I wanted a tripwire. Something that watches the sub and pings my phone when a post looks like one I could actually answer. It never posts anything. It never talks to anyone. It just tells me to go look.

What failed, in order

The obvious way in was closed. Reddit used to let you read a subreddit as JSON with nothing but a polite user-agent string. That returns a 403 now. Fine — get an API key. Except the app registration form bounced me back with a fresh captcha and no error, three times, before I went and read the policy: self-service API registration ended this year, and new access needs manual approval. Two transports dead in twenty minutes.

The one that worked was the oldest thing on the internet. RSS. The feed has been sitting there the whole time, no auth, no application, returning a clean 200. Every modern door was locked and the screen door was open.

The window was wrong by a factor of four. I’d written it to ignore anything older than six hours, picturing a firehose. Ran it, and the twenty-five most recent posts spanned fifty-two hours. It’s about a dozen posts a day, not a dozen an hour. So my six-hour window was throwing away three quarters of the sub — including, when I read the skip list, the exact post I’d answered by hand two days earlier. The thing I built to catch that case was discarding that case.

The clever part was the broken part. I’d written a filter to check whether a post named a place before spending any real thought on it — cheap regex, states and counties. It skipped a post about moving to Chicago, because Chicago is a city and my list had states. It would have skipped the original one too. I’d built a gate to protect a budget that turned out to be enormous relative to the traffic, and the gate was leaking the exact cases that mattered. Deleted it.

And then the semicolon. Two posts came back unreadable — a woman with a three-day eviction notice, and someone facing a foreclosure hearing on Friday. Both had been correctly judged as urgent. Both got thrown away, because somewhere in the output a semicolon appeared where a comma belonged, and a comma is what JSON requires. I’d written the parser to accept correct output. Real output isn’t always correct. It’s a punctuation mark, and it cost two people their place in the queue.

What I actually learned

Nothing on that list is something I could have thought my way to. Each one required running the thing and reading what came back. The 403, the fifty-two hours, Chicago, the semicolon — every single fix started as a surprise in a log.

That’s not a new lesson. It’s the same one as every framing job where the wall isn’t square and the plan assumed it was. You can be careful and thoughtful and still be wrong about the thing you couldn’t see until you cut it. The discipline isn’t planning harder. It’s building so the surprise is survivable — and then going and looking.

The one rule I put in early and kept is that every failure has to fail loud, in my direction. If the judgment step times out, or crashes, or returns garbage, the post still reaches my phone, marked as unjudged. A ping I didn’t need costs me ten seconds. A post that vanishes quietly costs somebody something I can’t get back for them. That asymmetry decides every design question in the thing.

Where it sits now

It runs on a small always-on machine here — polls the feed, keeps a queue, remembers what it’s already seen, and doesn’t wake me at three in the morning because my phone has a sleep mode and reimplementing that in software would have been silly. The judging half runs on my laptop, because that’s where the tooling lives, and it pulls work rather than being pushed at. That way there’s no code anywhere asking whether the laptop is awake. Sometimes it is, sometimes it isn’t, and the queue just gets deeper or shallower.

One thing still doesn’t work: the machine can’t reach the notification service, which is almost certainly a firewall rule I wrote myself at some point and have to go find. Turns out the fix is to run the notification service here too, which I wanted anyway for a reason I hadn’t thought through until partway through the day.

Because here’s the part that changed how I think about it. These posts are public — anyone can read them. But there’s a difference between a post that’s technically readable by everyone and actually read by forty people, and a tidy searchable table with names, locations, and a one-line summary of somebody’s worst week. The second one is a thing I built. It didn’t exist before I made it. Somebody fleeing a violent house has a hard time being found on a website with millions of posts, and a much easier time being found in an organized index.

So: it stays on my own hardware, the notifications don’t leave my network, and I’m going to stop keeping the text once a case is closed. That’s not paranoia. It’s the same reason a shelter doesn’t publish its address.

The part that isn’t software

The tripwire only tells me somebody’s there. Answering still means finding the right regional agency, checking the number’s live, and writing something that helps — twenty minutes a post, and about a dozen posts a day come through. That math doesn’t work, and pretending it does is how this quietly stops working in three weeks.

So the queue shows three at a time. No counter, no badge, no total of people waiting. I know that sounds like a small interface decision. It isn’t. A tool that makes you feel behind is a tool you stop opening, and the version of this that helps nobody is the version I abandon in a month because it made me feel like I was failing every time I looked at it.

Three at a time, answered properly, is better than forty half-answered. It’s the same reason I don’t start four projects on the property at once anymore.

Leave a Comment