Day 77: The Day After
Tuesday. The day after any release is the day the release stops being a release and starts being a deployment running on someone else's machine. The first ~24 hours of v0.5.0 in the wild surfaced three small but real things: a feature documented in the release notes that didn't actually work, a middleware that was over-zealous on its own internal callbacks, and a stack of docs prose that had drifted in the rush to ship.
BETTER_AUTH_URL Didn't Pass Through
The bug is the kind of thing you only catch once someone follows the install guide on a brand-new domain. v0.5.0's upgrade notes recommend setting BETTER_AUTH_URL in .env for HTTPS deployments — the canonical URL Better Auth uses to generate absolute links in password resets, OAuth callbacks, and Telegram pairing redirects. The Pinchy container, however, wasn't receiving the variable. docker-compose.yml didn't pass it through, .env.example didn't list it, the CI and release workflows didn't surface it, and the install guides told you to set a variable that the container couldn't read.
The fix is small and lives in five files at once, because that's the shape of a single configuration variable threading through every layer of an app. docker-compose.yml gains a BETTER_AUTH_URL environment passthrough; .env.example documents the variable alongside the existing BETTER_AUTH_SECRET; the CI workflow and release workflow propagate it; the install and upgrading docs gain a section explaining when it matters and when it doesn't. A matching unit test asserts the auth config is consistent with what the docs claim — the kind of test that wouldn't have caught yesterday's miss but will catch the next one.
A small bit of code cleanup fell out of the same pass: packages/web/server.ts was reading the variable in seven lines that did nothing useful (the framework reads it natively from process.env), and those got removed. The cleanup matters because the leftover code was actively misleading: it looked like the variable was being consumed when in fact the consumption was happening one layer up.
Internal Routes Hit the Domain Lock
The domain lock — SITE_HOST, the feature added in the v0.4.x era that rejects any request whose Host header doesn't match the configured canonical hostname — was over-applying itself. The middleware was rejecting internal callbacks too: requests that originate inside the Pinchy network, where the Host header is the Docker service name (pinchy:3000) rather than the public hostname. In practice, this affected the OpenClaw → Pinchy webhook for plugin-loaded events and the secrets-bundle re-fetch path. Both fired with the right intent and got a 403 from the gate that was meant to keep external attackers out.
The fix is to extend the same path-prefix exemption the CSRF gate uses (/api/internal/*) to the domain lock too. A request whose path lives under that prefix is an in-cluster caller by construction — the prefix isn't exposed to the public reverse proxy — so applying a Host-header check to it is just noise. Two new tests landed alongside: a domain-restriction unit test that asserts /api/internal/* bypasses the gate while everything else still hits it, and a deployment healthcheck test that asserts the in-cluster network paths still work end-to-end with the lock enabled. The end-to-end test is the one that would have caught yesterday's slip if it had existed two days earlier.
The Post-Release Docs Pass
The third thread today is a docs polish that's mostly typography and prose accuracy. The v0.5.0 release notes had a handful of cross-references to sections that got renamed during the Saturday hardening pass; the noupdate tip in the Odoo deploy guide had a stale path; the upgrading guide's sed command was hard-coded to v0.5.0 in a way that would be wrong by next release. The fixes are small individually and matter cumulatively, because the time someone reads a deploy guide is the time their tolerance for inconsistency is the lowest.
One of the docs commits closes the PR-review loop on PR #278 — the secrets-bundle architecture page from last week. The reviewer caught two places where the prose was claiming more than the code did (the bundle is per-deployment, not per-organisation; the rotation procedure is documented for the JWT signing key but not yet for the bundle key). Both got softened to match what's actually in the implementation, and a follow-up TODO for the bundle-key rotation procedure got opened against the next release window.
One CLAUDE.md Becomes Two Files
A quieter refactor in the same session: CLAUDE.md — the file that tells Claude Code what the project's conventions are — got carved into two files. The agent-agnostic contract (architecture, test conventions, security policies, release discipline) moved into a new AGENTS.md at 234 lines; the Claude-specific guidance shrank to a handful of lines pointing at AGENTS.md as the source of truth. The move was prompted by starting to use a second coding assistant in the same repo and discovering that a single CLAUDE-shaped file was the wrong shape for a tool that doesn't read CLAUDE.md.
The throughline of the carve-out is that the conventions belong to the project, not to the tool. An AGENTS.md that any agent can read is a single source of truth about how we build; a CLAUDE.md that lives next to it is the per-tool overlay. Same shape as the plugin manifest contract from yesterday — the agreement is on the manifest, the implementations are interchangeable.
Day 77
The day after a release is rarely a story; it's a checklist. Of the three small things today, two were docs claims the implementation hadn't quite caught up to, and the third was a middleware that was correctly aggressive in a way that needed a path-prefix exemption to be useful. None of them is a regression in the sense that anything used to work and stopped working — they're the seams that only get tested once the install base widens past the team. The shape of v0.5.x's first week is going to be more of this, and the hotfix that lands first is the one that catches the most surface area: tomorrow's v0.5.1 will likely be a single small thing too.