Blog
Technical notes on web development, DevOps, and AI integration.
216 articles
- 04:41tooling
Tesseract OCR in a pipeline: ordering, quarantine, empty output
Adding tesseract to an ingest pipeline turned out to be less about the CLI and more about ordering, the img caption convention, quarantine, and empty output.
TL;DR: A media ingest job stalled with media-blocked status after tesseract went unregistered, previously blocking every job including audio-only ones. The fix narrows preflight so OCR is only mandatory for image-only jobs, splitting failures into plan-blocking versus quarantine-worthy problems. Empty OCR output counts as success with evidence recorded, while infrastructure timeouts retry and corrupt content gets quarantined for healing.
#tesseract#ocr#python - 04:03devops
Event Log Integrity the Git Way: A Frame Tamper Story
A one-byte frame tamper taught me how content-addressed sidecars, an append-only event log, and disk-truth verification keep a pipeline recoverable.
TL;DR: When a corrupted frame triggered media-quarantine instead of a crash, the kernel proved it rebuilds state from events and verifies files from disk, never trusting recorded hashes. Content-addressed caption sidecars give free deduplication, while the append-only log enables clean recovery through full rebuilds. But hash chains prove tamper-evidence, not authenticity—integrity is an architecture, not a checksum ritual.
#git#event-sourcing#automation - 02:55ai
A valid MP4 that will never transcribe
A video file can be perfectly valid and still carry no audio. A nonstate skip keeps the row visible and holds the pipeline until someone decides.
TL;DR: A supposedly broken MP4 was actually valid, just with zero audio streams. The pipeline now runs ffprobe first, stores an audio_track flag, and emits a visible skip that never marks work done. Frames follow a fixed fps cadence with capped extraction, while audio is downsampled to 16kHz mono WAV for transcription.
#ffmpeg#whisper#ffprobe - 01:57backend
Lock stolen mid-run: what fencing tokens taught me
A lock checked once at startup guards the door, not the data. Fencing tokens validated at every state write, plus a heartbeat lease, are what guard the data.
TL;DR: A force-killed transcribe process showed why one-time lock checks fail: after the lock expired, a second session took over and idempotent resume reused finished windows from sidecars. The fix is fencing tokens, checked at every state write, so stale sessions die with lock-stolen-mid-run. Minimum package: TTL lock, heartbeat, and a negative control proving the guard fails.
#distributed-systems#idempotency#testing - 01:14ai
I finally split my transcription failures into two families
My transcription pipeline died silently mid-window. A hang turns out to be an exception that must be caught, and every failure has an owner.
TL;DR: After a hung transcription exposed an uncaught TimeoutExpired exception, the author split pipeline failures into infrastructure issues, which silently retry, and content issues, which log a failure event. Broken files follow a retry ladder capped at three attempts, then a visible skip that still blocks completion. Correct exit-code mapping keeps retries from amplifying hopeless failures.
#whisper#ffmpeg#python - 00:15ai
The Claude Code cost brake turned out not to be the turn limit
I thought --max-turns was keeping my delegation costs down. It only counts rounds; the flag that actually holds dollars is --max-budget-usd.
TL;DR: A test revealed Claude Code's --max-turns flag doesn't limit spending; a single-turn task cost $0.045 without ever touching its cap of two. By contrast, --max-budget-usd actually halted a run mid-turn once spending passed $0.01. The takeaway: use budget caps to control costs, keeping turn limits only for stopping endless loops.
#claude-code#cli#llm - 23:34ai
The transcript filter is a projection, not a parameter
Changing a filter rule doesn't mean re-running whisper. The filter is a projection of stored stdout, stamped with a sha256 of the rule.
TL;DR: When the profanity filter changed, the author's first instinct was re-running Whisper on every transcript, even though the audio never changed. The fix: treat filtering as a cheap projection of already-cached Whisper output, not a transcription step. Stamp artifacts with a phrase-list hash, so rule changes mean instant re-projection with zero model calls.
#python#whisper#idempotency - 18:21devops
When Custom Docker Images Break Your Upgrade Playbook
Fleet-wide custom Docker images make digest-based upgrade playbooks obsolete; verification shifts to app endpoints, upgrade path becomes clone→build→transfer.
TL;DR: Locally built Docker images broke my Ansible upgrade playbook, since digest comparison needs a remote registry image that custom builds never have. Automation has permanently forked for these hosts. Upgrades are now manual: build locally, transfer via scp, docker load, then swap containers with about 15 seconds downtime while data mounts stay untouched.
#docker#ansible#automation - 17:47devops
Build Docker Images Off-VPS When the Official Registry Lags
The Docker Hub image lags behind the npm release. Build on a big machine, ship it with save, gzip, and scp, then load it on a small VPS.
TL;DR: The Docker Hub image trailed the npm release, and a 964MB VPS can't build a Next.js app without the OOM killer intervening. The fix: build locally, save and gzip the image, scp it over, and docker load it, keeping the old container renamed for instant rollback. Custom images carry different digests, so digest-checking playbooks must skip this server.
#docker#deployment#ssh