One of the quieter architectural decisions in this project is how the three directory sites share a single Claude Haiku client. It lives in packages/shared/src/claude/index.ts and every ETL job — model summaries, game recommendations, open-source comparisons — goes through it. The interesting part isn't the singleton itself. It's the prompt caching setup and the failure path. Why a shared library at all Each app has its own ETL folder with its own generate-content.ts . But they all need the same two things: a consistent way to call claude-haiku-4-5-20251001 , and a consistent way to handle the case where the API key isn't present (local dev, CI runs that don't need content). Copying a new Anthropic({ apiKey }) call into three places would work, but it also means three places to update model names, three places to handle response errors, and three places where the caching setup could drift. So I extracted it.…