Architecture
Three sources of truth, one verify
blogus never sits in your runtime path. It reconciles three artifacts at build time — the marker in your code, the .prompt file on disk, and the entry in prompts.lock — and fails loudly the moment they disagree.
The three artifacts
1 · Code marker
A # @blogus:name sha256:… comment above the load_prompt() call. It records which prompt the call site expects and its hash.
2 · .prompt file
YAML frontmatter (name, model.id, model.temperature, variables) plus a template body. The reviewable unit — this is what shows up in a diff.
3 · prompts.lock
One entry per prompt: a sha256 over the template content and the git commit it was generated from. The pinned record of what was approved.
The pipeline
your code prompts/ prompts.lock
───────── ──────── ────────────
chat.py ──scan──▶ detect inline LLM calls
│
init/version
▼
summarize.prompt ──lock──▶ summarize: sha256:a1b2…
│ commit: 4903f76
fix│
▼
load_prompt("summarize")
# @blogus:summarize sha256:a1b2…
┌──────────── verify ────────────┐
│ recompute hash on disk, │
│ compare marker == file == lock │
│ exit 0 if all agree, else 1 │
└──────────────────────────────────┘ Why build-time, not runtime
A hosted prompt registry answers a request-time question: “what is the current prompt for name?” That puts a network call — and a moving target — in your critical path. blogus answers a different, earlier question: “does the prompt about to ship match the one that was reviewed?” Because the answer is a pure function of files already in your repo, it needs no server, no API key, and no network. The verify step is deterministic and offline.
What verify actually checks
blogus verify loads every marker in the code, hashes the referenced .prompt file on disk, and compares that against the entry in prompts.lock. Three-way agreement means the running code, the source of truth, and the approved record are the same bytes. Any disagreement — a hand-edited prompt, a stale lock, a marker left behind after a rename — is a non-zero exit. That single exit code is the whole CI contract.
Where it slots into your pipeline
- Pre-commit: run
blogus checkto catch inline strings before they’re committed, andblogus verifyto catch stale locks. - CI:
blogus verifyas a required check — no secrets needed, since it never calls a model. - Release: the lock’s commit field ties each pinned prompt back to the exact ref it was cut from, so a release is auditable after the fact.
Boundaries by design
blogus deliberately does not fetch prompts at request time, collect traces, or run a hosted service. Runtime commands (exec, analyze, test) are the only ones that touch a model, and they read OPENAI_API_KEY / ANTHROPIC_API_KEY from the environment. The build-time path — scan, init, lock, verify, fix, check — is pure file I/O.
See the workflow run
The quickstart shows each command in order; the glossary defines the terms above.