· Automation and agents · Content strategy · Brand safety · Production cost · 5 min read

Build a Content Engine That Can Veto Its Own Writer

The real prompts behind our pipeline: a Sonnet writer, a Gemini critic that vetoes it, an editor with a kill switch. Copy each into your Claude Code.

A complete matte-white porcelain head faces a second head still forming out of a gold triangulated wireframe, a low gold barrier dividing them, gold spheres dispersing to the right.
One role finished, one still forming, a gate between them — separation of powers, drawn rather than described. Illustration generated in the AIAvatar house style (Gemini 3 Pro Image).

The bug that faked the exact confidence it was built to catch

In run mrs3ooy6, our critic flagged three real passages in a draft — lines 44, 64, and 97 — and the editor called the catch a hallucination. The critic had not hallucinated. The editor was reading the wrong draft: the critique described draft-1, the revision had already removed all three passages, and the editor searched for them in draft-2, found nothing, and shipped a verdict of publish on the theory that the critic had imagined its own findings.

That is the whole reason to build a content engine as a team with a veto instead of a chain of “make it better” prompts — and the whole reason the wiring between the roles is where it actually breaks. Below are the exact role prompts we run, verbatim, and the one-line fix that stops the editor judging the wrong draft. Copy them into your own Claude Code.

What are the seven roles, and which two can stop an article?

Seven roles touch every article; only two can stop one. The editor-in-chief can decline to commission a piece and can kill a finished draft. The fact checker can block a draft that carries an unverifiable claim. The other five can only revise — they make a draft better, they never make it not ship.

The newsroom: who does what, and who holds a veto
RoleCan stop the article?Model family
WriterNo — drafts onlyClaude Sonnet
Editor-in-chiefYes — commission & killClaude Sonnet
Fact checkerYes — blockGemini Flash
CriticNo — argues, cannot vetoGemini Pro
SEO editorNo — structure onlyGemini Flash
Art directorNo — pictures onlyGemini Flash
DistributorNo — post-publishClaude Sonnet
The newsroom: who does what, and who holds a veto scripts/engine/newsroom.mjs (committed) · 2026-07-20

Why must the critic run on a different model family?

Because a model reviewing its own output shares its own blind spots. It rates its own ambiguous phrasing as clear — it wrote the phrasing — and it agrees with its own reasoning because the reasoning is its own. Our writer is Claude Sonnet; the critic is Gemini Pro. That pairing is the mechanism, not a preference.

A critique from the same model family is a comment. A critique from a different one is a second opinion. Only the second one is worth paying for.

Seven identical matte-white porcelain spheres, each with a distinct gold wireframe pattern, arranged in an ascending line on a seamless white background.
Seven roles per article; two of them can stop it — and one of them was reading the wrong draft. Illustration generated in the AIAVATAR house style (Gemini 3 Pro Image).

Build it — the three prompts that carry the whole thing

Reproduce this in your own repo

  1. Save the shared constitution to ~/.claude/engine/CONSTITUTION.md. Every role is handed this text before its own instructions — a rule the writer has but the critic does not have is a rule the article does not actually follow.

  2. Save the critic prompt to ~/.claude/engine/critic/SOUL.md and run it on a different model from your writer. This is the block that pays for itself.

  3. Save the editor prompt to ~/.claude/engine/editor/SOUL.md. This is the kill switch. Without it, a pipeline’s only possible output is an article, and what it produces when it has nothing to say is exactly the commodity content that gets a whole site classified as a farm.

The constitution — what every role is told first

Save to ~/.claude/engine/CONSTITUTION.md
The five rules (verbatim)
THE FIVE RULES. None is negotiable, and each has a specific failure behind it.

1. NEVER INVENT A NUMBER.
   Not a price, a percentage, a date, a rate, a benchmark, a market size, a
   study result, or a quotation. Not "roughly", not "typically around", not "up
   to". If a figure is not in the approved fact registry or in a source you were
   given, you may not write it. Write the sentence without the number, or write
   "we have not measured this".
   A model asked to sound authoritative WILL fabricate statistics. This is the
   default failure mode of this whole system, not a hypothetical.

2. EVERY ARTICLE CARRIES SOMETHING A LANGUAGE MODEL COULD NOT HAVE WRITTEN.
   A measurement we ran. A client result with real figures. A limit read out of
   a vendor's own documentation and quoted. A test log. An article whose value
   could be reproduced by anyone typing the same prompt has no reason to exist
   and will not be published.

3. RUSSIAN IS WRITTEN, NEVER TRANSLATED.
   The Russian article is a different article for a different reader: different
   platforms, different tools, different constraints. If a Russian draft reads as
   an English one restated, it has failed.

4. NO PADDING.
   No "In today's fast-paced digital landscape". No section that restates the
   previous section. No "it's important to note that". If a paragraph could be
   deleted without loss, delete it. Length is an outcome, never a target.

5. SAY SOMETHING FALSIFIABLE.
   An article that could not possibly be wrong is an article that says nothing.
   Take a position, name what would change your mind, and say plainly where you
   disagree with the consensus in your field.

Prepended to every role's system prompt. Paste it above any of the prompts below.

The critic — the block that earns its cost

Save to ~/.claude/engine/critic/SOUL.md
The critic's system prompt (verbatim)
You are the critic. You did not write this and you are not trying to be kind.
You run on a different model family from the writer specifically so that you
fail to share its blind spots — use that.

Answer, concretely and with quotations from the draft:

 1. Would a busy operator forward this? Quote the single passage that would
    survive being sent with no context. If there isn't one, say so — that is
    the most important finding you can report.
 2. What here could any language model have produced without our data? Quote
    the commodity passages by name.
 3. Where does it hedge instead of committing? Quote each instance.
 4. What does an informed reader already know that this spends words on?
 5. Where does the argument actually break — not stylistically, logically?
 6. What obvious objection goes unaddressed?
 7. Is the opening the most interesting true thing in the piece? If not, name
    what is and say where it currently sits.

Then: three specific cuts, and the one change that would most improve it.

Do not praise. Do not summarise the article back. Do not suggest adding a
"conclusion that ties it together". Every point cites a quotation.

Paste it after the constitution, then paste a draft. It returns seven concrete answers, each citing a quotation. Runs as-is in one Claude Code session.

The editor — the kill switch

Save to ~/.claude/engine/editor/SOUL.md
The editor-in-chief's verdict prompt (verbatim)
You are the editor-in-chief, deciding whether this ships. You have the brief you
wrote, the final draft, the fact checker's report and the critic's report.

You are not summarising your colleagues. They report; you decide, and you may
overrule either of them in either direction — with a reason.

PUBLISH only if all of these hold:
 · The non-commodity asset from the brief is actually present in the text, not
   merely referenced.
 · The fact checker found no unsupported or contradicted claim.
 · There is at least one passage a reader would forward.
 · The article commits to something that could be wrong.
 · It is not padded.

Otherwise REVISE with specific instructions, or KILL. Killing is a real option
and costs less than publishing something that makes us look like everyone else.

Say plainly what is weakest about this article even when you publish it.

Paste the brief, the draft, the fact checker's report and the critic's report after it. It returns publish, revise, or kill.

The wiring fix — hand the editor a critique of the draft it is actually judging

The bug in run mrs3ooy6 was not in any prompt. It was in the loop: the critique was written against draft-1, the revision then removed exactly the passages it flagged, and the editor was handed that stale critique next to draft-2. The fix is one extra critic call on the revised draft, so the report the editor reads describes the text the editor is reading.

Save to scripts/engine/pipeline.mjs
// Re-critique the REVISED draft, and hand the editor THIS critique instead of
// the one written against draft-1. One extra critic call on a revision is cheap
// next to an editor reasoning from a mismatch — which is how a pipeline built to
// catch confident errors ends up publishing one about itself.
critique = run.record(
  'critique-2',
  await role('CRITIC', `BRIEF:\n${briefText}\n\nDRAFT:\n${draft}`, run),
);

Ships verbatim from our repo. The one extra call converts an editor reasoning from a mismatch back into an editor reasoning from evidence.

Run it — what one article costs, and what the loop refuses to do

One article through the full pipeline collapses to 9data/runs/*/run.json (committed) model calls when no second revision is needed, and costs $0.32–0.35data/runs/*/run.json (committed) for roughly 1,500 words, measured across the first three runs. The expensive tier is judgment — the writer, the editor and the critic — and it is 97% of the spend; the cheap model does the checklist work.

Run
OPENROUTER_API_KEY=YOUR_OPENROUTER_KEY \
  node scripts/engine/run-article.mjs data/briefs/YOUR_BRIEF.json --locale en

The full loop, not a paste. Needs the repo, Node, and one OpenROUTER key that routes to both Claude and Gemini. Reads a brief JSON, writes every stage to data/runs/<id>/.

For the first article this pipeline ever published, that loop returned a verdict of publish only after 2data/runs/2026-07-19-en-mrs3aw6f, -mrs3iijt, -mrs3ooy6 (committed) — sent back once by the editor, once by the fact checker. Neither role added a fact the company did not already have. They only refused to ship without one.

Where it breaks — the honest limits

None of this makes the writing good. The prompts remove commodity passages and unsourced numbers; they do not supply an idea. And the fix above exists because the pipeline did not catch its own draft-ordering bug — an adversarial read of the committed logs did, weeks later, while reviewing an earlier article that had told a flattering, false version of this same story.

If you paste the three prompts above into your own Claude Code and run a draft through them, you will get the same thing we get: fewer invented numbers, fewer commodity paragraphs, and a verdict you can argue with. The value was never a better prompt. It was going back to the logs.