Build LogBBBrain
#231
2026-06-075 min read

Sender reputation first: why the Inbox Hub has no messages table

Building an inbox triage surface meant deciding what goes in the primary lookup table. The answer wasn't messages — it was senders. Five SQLite tables, a partial index on pending unknowns, and a 60-second badge later, the schema design is the product.

Read post
Build LogDevOps
#232
2026-06-066 min read

I built six sprints of article scheduling. For four of them, nothing fired.

Four sprints of Telegram button wiring, queue state machines, and free-text time parsing — all recording intent into `article_queue` without dispatching a single article. Sprint 5 was the first one that actually fired. Separating mechanism-to-record from mechanism-to-execute isn't a new idea; applying it deliberately at the sprint level is.

Read post
Build LogDVLAW
#233
2026-06-055 min read

The schedule queue took four sprints to remember. One to act.

After four sprints of B-schedule, a scheduled article existed as a database row with a correct timestamp. No article had published. Sprint 5 added the sweep daemon and publisher_lock. That is when scheduling became real.

Read post
Build LogDVLAW
#234
2026-06-045 min read

A missed slot at 09:00 should not silently vanish: building past-due grace into the article pipeline publisher

The `schedule_sweep` runs on a laptop. Laptops sleep. Without a grace policy, an article scheduled for 09:00 silently strands when the machine wakes at 09:07. Sprint 6 of B-schedule — past-due grace plus 48-hour hard-expire — turns that silent drop into a recoverable state with a reason, an audit trail, and a path back to publish.

Read post
Build LogPython
#235
2026-05-318 min read

The classifier is the product: how four branches replaced an inbox

Inbox Hub is five SQLite tables, twelve route handlers, four Shadow DOM components, and a launchd cron. Strip those away and what remains is a four-branch decision tree that runs on every message. Everything else is plumbing around that core.

Read post