Doc-status state machines
The problem: work that spans multiple actors — a user requests something, a server process picks it up, an external worker does a slow step, something posts the result. Traditionally: a queue, a consumer, a status API, and a dashboard to see what's stuck.
The pattern: one document per unit of work, with a status field walking
a small state machine. Every actor reads the statuses it owns, does its step,
and writes the doc forward.
The platform's own mention-builds bot
(the story) coordinates three actors this
way, through a mention doc in its requests database:
pending-build → building → built → replied
with terminals: skipped (guardrails), build-failed, error
- The vibe's
scheduledhandler ingests Bluesky mentions and writespending-builddocs (after guardrails — dedupe, caps, moderation). - A GitHub Actions cron picks up
pending-buildvia the CLI, flips it tobuilding, runs codegen, writesbuilt(orbuild-failed). - The next scheduled tick sees
built, posts the in-thread reply, writesrepliedwith the permalink.
Same shape in meta-hub's publisher:
publish-request docs walk pending → items-created → carousel-created → done | error, and you can inject work from anywhere the CLI runs:
shnpx vibes-diy db put --vibe you/your-hub --db requests '{
"kind": "publish-request", "platform": "ig", "slug": "my-post",
"images": ["https://…/slide-1.jpg"], "caption": "…",
"status": "pending", "attempts": 0, "createdAt": "2026-07-07T00:00:00Z"
}'
The result lands on the same doc a few ticks later — status, permalink,
or a lastError you can actually read.
Rules that keep it honest
- One writer per status. Decide which actor may write each transition
(the builder lane writes
building/built/build-failed; the vibe writes everything else). Enforce the coarse boundary inaccess.js; keep the fine-grained transition table in code and in the doc's comment. - Statuses are terminal or owned — never ambient. A doc sitting in
buildingfor 45 minutes means its worker died; the next worker run retries it, bounded by anattemptscounter on the doc. Timeouts and retry budgets live on the document, visible to the dashboard. - Failures are states, not exceptions.
build-failed,error,skippedare first-class: nothing disappears, and the ledger doubles as the audit log. - Idempotent ids. Derive
_idfrom the upstream event (the Bluesky record key, the request slug) so re-ingesting is a no-op instead of a duplicate.
What you get for free
Because the "queue" is a Fireproof database: the dashboard is a
useLiveQuery over the same docs (see
the operator console pattern), every
transition syncs live to whoever's watching, the access function gates who
can inject work, and the CLI gives any script or CI runner authenticated
access with no extra API (the CI lane pattern).
Two actors never need to share a secret — they only share document shapes. The Bluesky credential in mention-hub never leaves the vibe's vault; the CI runner holds only a device cert. The database is the interface.