Producer Fallback: When Claude Hits the Weekly Limit, the Pipeline Still Ships A developer building an automated daily content pipeline implemented a fallback mechanism after Claude hit its weekly rate limit, causing a missed post. The pipeline now auto-commits session audit logs to maintain a clean tree and fails closed on AI voice fingerprints like em dashes. It supports fallback to Grok when Claude is unavailable, ensuring a post is published every day regardless of model outages. A daily content pipeline that dies because one model returns a rate-limit error is measuring the wrong success criterion. The job is not "Claude ran." The job is "yesterday has a post on the live site." On 2026-07-15 that distinction stopped being theoretical. Two failures, one after the other: .beads/interactions.jsonl . preflight branch normalize correctly refused to run on a dirty master . No produce step. No post for 2026-07-14. claude -p /blog-backfill exited in three seconds with: weekly limit, resets Jul 19. Land saw no post file and reported NO-POST rc=20 .That is the same shape as automation assurance on Intent-OS: exit codes and "the scheduled job ran" are not outcome verification. The outcome is a live URL. .beads/interactions.jsonl is an append-only session audit log. Any bd close dirties it without committing. Treating that as "human has uncommitted feature work" is wrong; treating real content dirt the same as always is right. preflight branch normalize now: .beads/interactions.jsonl , auto-commit it with a mechanical message and continue.That keeps the clean-tree invariant for posts and methodology files without letting a late-night bead close silence the morning publish. Separately, the produce path now fails closed on AI voice fingerprints: em dash / en dash hard ban, expanded slop phrase list, deterministic lint-post-voice.py in the skill and again in blog-land.sh . Phrase checks mask fenced code and URLs so repo names do not false-positive. Historical posts are not bulk-rewritten; only the post being landed is gated. That gate is what made the recovered 2026-07-14 post ship without the em-dash density that had become the house default. Claude remains the primary producer. It is not the only producer. blog-backfill-daily.sh now: claude -p /blog-backfill same as before . decisions.jsonl , write .blog-staging/DATE.intent.json with ready:true only after gates including voice lint . No git. blog-land.sh afterward verify → commit → push → dual-publish → queue, or quarantine .Env knobs: | Variable | Values | Meaning | |---|---|---| BLOG PRODUCER | auto default , claude , grok | Which producer s to run | GROK BIN | path | Defaults to ~/.grok/bin/grok | BLOG GROK MAX TURNS | int | Headless turn cap default 120 | auto is the production setting until Claude is healthy again, and it stays useful after: a weekly limit or API blip should not equal a missed day. While Claude was dark, the 2026-07-14 post was produced manually under the same land contract: The content thesis of that post and the ops lesson of this one are the same sentence: exit 0 is not success; verify the outcome. master . v2.1.6 from the overnight auto-release path: docs-only, not the story of the day.Producer outages will happen. Rate limits, model downtime, auth glitches. The pipeline should treat them like any other dependency failure: Tomorrow's 04:00 run can try Claude, fall back to Grok, and still land. That is the bar.