I Raised the QA Bar on Blog Infographics
Blog charts need correct labels. I tightened layout geometry, char budgets, claims checks, and vision logs so a bad PNG cannot ship quietly.
I ship measured local-model posts with charts. Generative image tools garble numbers. So the fleet uses one matplotlib renderer under config/visuals/. Exact text. Fixed brand colors.
That still was not enough. A chart could pass "it rendered" and still look sloppy: numbers in corners, tiny labels, digits that never appear in the post. I raised the bar.
What failed before
The first defect class was layout. Step numbers sat in the top-left of a card while the label sat in the center. Peer cards did not share baselines. Long labels shrank below a readable floor.
The second class was honesty. A graphic could invent a comparison that no metrics file contained. Vision QA only checked whether the image looked clean, not whether the numbers were true.
The third class was publish mechanics. Committing a PNG from a feature branch worktree was easy to get wrong. Agents need a path that always targets main for the one asset file.
The gates
Every blog infographic now hits the same chain before an embed line is printed.
- Char budget. Budgets scale with step count. A short chain (n≤3) gets wider cards and longer labels. A four-step row stays tight (18/22). Five or more steps use a two-row grid so cards do not crush.
- Layout then claims. Numbers, labels, and subs share peer baselines. Step indexes sit in orange pills with clearance so they never paint over the words. Digits in the graphic require
--claims <post or metrics>or publish exits 8. - Vision log. The vision gate still fail-closes. Full stdout and stderr land under
Reports/Visuals/so a timeout is not a mystery.
Footer text stays at the same readable floor as body labels (7pt minimum). Titles sit on a dark header scrim so ambient textures cannot wash them out.

What I measure
The unit suite under config/visuals/ is at 29 tests as of 2026-07-13. Selftest renders every file in examples/ and specs/ (13 specs after this post). Geometry QA checks containment, min size, peer baselines, and peer line counts. Char lint runs before draw.
I also re-rendered the live vector-migration post asset earlier the same day after the first stack fix. New posts get the full gate set automatically when they use publish_blog_asset.py.
What I will not do
I will not put labels inside a diffusion model. Charts need crisp, correct English. Ambient textures stay under the diagram. Text stays on the matplotlib layer.
I will not ship a PNG that fails any gate. Fail closed means ship text-only or fix the spec. That is intentional.
When a graphic is worth it
Use a chart when the post has a process, a comparison, or a set of numbers. Write the short labels first. Point --claims at the draft. If the story needs long phrases, use fewer steps or a stats/flow template. Do not fight the 4-up width with paragraphs inside cards.
The contract lives in Prompts/Includes/blog-publish.md and config/visuals/README.md. Agents read it before inventing a one-off plot.
Accompanying prompt
What the prompt does: This prompt turns a draft post into a fail-closed infographic publish checklist with char budgets and claims.
Copy/paste this prompt:
Copy-ready prompt
Paste the exact block into your coding agent.
No article chrome, no footnotes, no formatting drift.
This prompt and every other one we publish live in the free prompt library.
Copy the block above.
I publish measured local-model work and the systems around it in The 5090 Reports. Join the list for the next artifact.
For related layout and local-model posts, see I rebuilt 5,383 embeddings after a dimension change and the num_ctx reload tax.
Get the local AI lab notes
Benchmark rows, VRAM fit checks, quant choices, and what actually runs on consumer GPUs. M-F, only when there is something worth sending.
Patrick Hughes
Building BMD HODL — a one-person AI-operated holding company. Nashville, Tennessee. Twenty-Two agents.
More writing
- 6 min
I Rebuilt 5,383 Embeddings After a Dimension Change
I moved a vault search index from 1,536 to 768 dimensions. Here is why I rebuilt all 5,383 chunks instead of mixing vector spaces.
- 6 min
Pin Your Local LLM Context Size Before You Build a Router
Changing context size can reload a local model before every request. A measured RTX 5090 sweep shows why context belongs in the routing key.
- 5 min
Local LLMs Need a Timeout Before They Need a Bigger Model
A bigger local model will not fix a stuck runtime. Add a bounded inference doctor first, then trust the benchmark.
- 5 min
A verifier loop beats a faster local model
Local LLMs are useful when the loop proves the output, not when the benchmark looks good. This is the small gate I use before a local coding agent gets more rope.
- 4 min
How I Gate a Local Coding Model Before I Trust It
A local model is not ready because it runs fast. It is ready when one verifier loop can prove the output before an agent writes files.