---
id: "6.8"
title: "Safety and refusals"
dept: ai-and-models
status: draft
owner: Param
branded: false
last_verified: 2026-10-26
sources:
  - "brew origin/main b91e364: ai.js:34-45, 71 and 82, gemini-client.js:28 and 91-94, image-guard.js:43-50 and 60-72, media.js:50-59, vertex-media.js:83-85, jam2/engines.js:66-71 and 131-139, jam2/higgsfield.js:65-84, jam2/routes.js:70, jam2/studio.js:88 and 301, drafts.js:183, finals.js:104-111 and 130-138, draft-quality.js:97 and 108, sloosh-prompt.js:37, claims.js:88, reader/adlib.js:37 and 54, community-gate.js:181, demo-mode.js:43, budget.js:71-77"
  - "memory ugc-factory-pipeline, brew-sloosh-finals, brew-rival-pictures, brew-viral-remakes, brew-draft-quality, brew-name-and-scope, brew-community-seat, brew-closed-store-gate, brew-access-rules, prod-clean-only, never-disconnect-arata-meta, brew-go-live-2026-10-17"
  - "handbook AGENTS.md rules 3 and 6"
---

# Safety and refusals

## Answer

What the models and makers refuse, what Brew refuses on purpose, and how each refusal is explained to a person in one plain sentence that says what happened, whether anything was charged and what to do next.

## Today

### What the makers refuse, and what Brew does

| Maker | What it refuses | What Brew does | Source |
|---|---|---|---|
| Claude | A refusal stop reason | Server-side fallback first; then the call throws `refused: <category>` and the caller decides | [src: brew/ai.js:71] [src: brew/ai.js:82] |
| Gemini text | SAFETY, PROHIBITED, BLOCKLIST, SPII, RECITATION, IMAGE_SAFETY, or a blocked prompt | Read as a refusal, same path as Claude | [src: brew/gemini-client.js:28] [src: brew/gemini-client.js:91-94] |
| Gemini image | A 400, 403, blocked or safety error | Not retried; a "no image" reply is retried up to 3 times | [src: brew/image-guard.js:43-50] |
| Veo | A filtered clip, `raiMediaFilteredCount` | Tried once with the plainest motion; never moved to the stronger model, which filters too; the draft keeps its still | [src: brew/vertex-media.js:85] [src: brew/media.js:50-59] [src: brew/drafts.js:183] |
| Veo, learned in the UGC factory | Sleep words with a woman on camera; some generated faces on input; Veo standard more than fast | Avoid those words; probe faces; use fast | [src: memory ugc-factory-pipeline] |
| Higgsfield | nsfw, failed or cancelled jobs | Refunded by Higgsfield; the next partner may cost up to 1.5 times the first price, so a false alarm does not leave the brand with nothing | [src: brew/jam2/higgsfield.js:65-84] [src: brew/jam2/engines.js:131-139] |
| Higgsfield video restyle | Reference footage with skin, showers or close-ups | Says so and offers the viral short, which follows the shots without sending the footage | [src: brew/jam2/studio.js:301] |
| Sloosh | A refused quote or asset, such as `owner_invalid_request` | Logged with what was asked; an image final falls back to Gemini, a video to Veo; credits are spent only once Sloosh takes a quote | [src: brew/finals.js:104-111] [src: brew/finals.js:130-138] [src: memory brew-sloosh-finals] |
| Anthropic billing | Credit used up | Every caller pauses 15 minutes, ops get one email a day, the UI says so; a placeholder never stands in on a live site | [src: brew/ai.js:34-45] |

### What Brew refuses on purpose

| Rule | How it is held | Source |
|---|---|---|
| No invented codes, guarantees, timed results, health or measured claims | Claims guard on every writer and every copyable surface | [src: brew/claims.js:88] |
| No fake experts | Anyone shown is an everyday customer; the look judge fails a lab coat, scrubs or a clinic | [src: brew/sloosh-prompt.js:37] [src: brew/draft-quality.js:97] [src: brew/draft-quality.js:108] |
| No fake reviews | A designed piece that styles words as a review or adds stars without a real review fails its read-back | [src: brew/jam2/creative.js:111-131] |
| No testimonials when the Brand Book says no | Style blocked | [src: brew/jam2/studio.js:88] |
| A rival's picture is never shown as ours, and never goes into a final | Rival tests in server and client | [src: memory brew-rival-pictures] [src: memory brew-viral-remakes] |
| A creator's words are never reused in our version of a viral video | Writer rule | [src: memory brew-viral-remakes] |
| Our version of a rival ad is a written breakdown only | Product rule since 17 Oct | [src: memory brew-go-live-2026-10-17] |
| No adult, quarantined or replica communities | Community gate | [src: brew/community-gate.js:181] [src: memory brew-community-seat] |
| No captcha solving | The Ad Library reader stops on a captcha | [src: brew/reader/adlib.js:37] [src: brew/reader/adlib.js:54] |
| Nothing posts, sends, schedules, pauses or spends in v1 without a person's yes | Finals and Studio makes need a yes on the price | [src: AGENTS.md rule 3] [src: brew/finals.js:1-4] |
| No writes to real accounts from the demo | `guardFetch` blocks ad, store and email writes and every send | [src: brew/demo-mode.js:43] |
| Nothing dummy or test on production | Clean gate before every promote | [src: memory prod-clean-only] |
| No payments, logistics or tax | Out of scope | [src: memory brew-name-and-scope] |
| A closed store is visible only to its team | Closed-store gate | [src: memory brew-closed-store-gate] |
| Arata's Meta and Shopify are never disconnected | `PROTECTED_CONNECTIONS=arata-in` | [src: brew/tools/prod-env.sh:9] [src: memory never-disconnect-arata-meta] |

### How we explain it

Each message says what happened, whether it cost anything and the next step. Examples on main:

| When | What the person reads | Source |
|---|---|---|
| A Studio maker's filter refuses | The makers' filters "sometimes misread hair, skin or close-ups", nothing was charged, press again or try another style | [src: brew/jam2/routes.js:70] |
| A video draft is filtered | The video model turned this frame down, so here is the picture; ask again for another scene | [src: brew/drafts.js:183] |
| A picture fails the look check twice | It didn't come out clean enough to show, with the reason; try again | [src: brew/image-guard.js:71] |
| A claim is cut | Why, such as "the code isn't on your store" | [src: brew/claims.js:68-81] |
| A day's budget is used | Which share is used and that it resets at midnight UTC | [src: brew/budget.js:71-77] |
| A video final misspells | Which word, where in seconds, and that a remake costs credits | [src: brew/text-check.js:50-55] |

## Tomorrow

| Item | Version | Tag |
|---|---|---|
| Every refusal says what happened, the cost and the next step | v1 | Stays |
| Refusal messages with no vendor names and no product name | v1 | Changes |
| A refusal log per brand, so repeated refusals on one product show | v1 | New |
| Platform policy checks before anything posts | v2 | New |
| Ad policy and spend rules before any ad change, inside the brand's own limits | v4 | New |
| Category rule packs kept by the team, such as ASCI and FSSAI | v1 | Stays [src: memory brew-brand-book] |

## How we do it

1. A new maker call catches the maker's refusal and turns it into one sentence with cost and next step; it never shows a raw error.
2. A refused paid job never moves to a dearer partner beyond the price shown, except within the 1.5 times room after a refund [src: brew/jam2/engines.js:131-139].
3. For Veo, keep sleep words out of dialogue and use the fast model [src: memory ugc-factory-pipeline].
4. Read a Sloosh refusal from the log line `[finals] Sloosh refused` or `GET /api/ops/sloosh` [src: memory brew-sloosh-finals].
5. Never retry a refused prompt in a loop; one plainer retry at most.

## Numbers

| Metric | Definition | Where measured | Status |
|---|---|---|---|
| Refusal rate by maker | Refused jobs over jobs, per maker and day | engines and finals logs | Proposed |
| Charged refusals | Refusals that cost credits or dollars | finals records | Proposed, target 0 |
| Sloosh refusals kept | Last 12 in memory per instance | `/api/ops/sloosh` [src: brew/finals.js:107-111] | Live |

## Open

1. **Product name in messages.** Some messages say "Brew", and some name Higgsfield, while product rules say no product name and no vendor names on founder screens [src: brew/drafts.js:183] [src: brew/jam2/studio.js:301] [src: AGENTS.md rule 6]. Proposal: one copy pass over every refusal and budget message.
2. **Refusal log.** Refusals are kept only in memory on one instance. Proposal: store them per brand and month beside the spend file.
