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.