{
 "id": "6.8",
 "title": "Safety and refusals",
 "department": "AI and models",
 "department_slug": "ai-and-models",
 "direction": null,
 "branded": false,
 "status": "draft",
 "owner": "Param",
 "last_verified": "Mon Oct 26",
 "holds": "What the makers refuse, what we refuse, how we explain it",
 "urls": {
  "html": "https://handbook.joinsquareup.com/ai-and-models/safety-and-refusals",
  "markdown": "https://handbook.joinsquareup.com/ai-and-models/safety-and-refusals.md",
  "json": "https://handbook.joinsquareup.com/ai-and-models/safety-and-refusals.json"
 },
 "other_directions": [],
 "tldr": {
  "headline": "When a maker says no, we explain it in one plain sentence",
  "points": [
   {
    "icon": "lock",
    "text": "Brew refuses invented claims, fake experts, fake reviews and rival pictures passed off as ours."
   },
   {
    "icon": "chat",
    "text": "Every refusal says what happened, whether anything was charged and what to do next."
   },
   {
    "icon": "gear",
    "text": "Refused jobs fall back to the next partner, within 1.5 times the price shown."
   },
   {
    "icon": "flag",
    "text": "Nothing posts, sends, schedules or spends in v1 without a person's yes."
   },
   {
    "icon": "warn",
    "text": "Some messages still name Brew or Higgsfield, and refusals live only in memory."
   }
  ],
  "stat": {
   "value": "15",
   "label": "minute pause, credit used up"
  },
  "decide": "Run one copy pass over every refusal and budget message to drop product and vendor names?",
  "mood": "building"
 },
 "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"
 ],
 "open_questions": [
  "**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.",
  "**Refusal log.** Refusals are kept only in memory on one instance. Proposal: store them per brand and month beside the spend file."
 ],
 "parts": {
  "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\n\n| Maker | What it refuses | What Brew does | Source |\n|---|---|---|---|\n| 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] |\n| 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] |\n| 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] |\n| 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] |\n| 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] |\n| 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] |\n| 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] |\n| 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] |\n| 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] |\n\n### What Brew refuses on purpose\n\n| Rule | How it is held | Source |\n|---|---|---|\n| No invented codes, guarantees, timed results, health or measured claims | Claims guard on every writer and every copyable surface | [src: brew/claims.js:88] |\n| 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] |\n| 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] |\n| No testimonials when the Brand Book says no | Style blocked | [src: brew/jam2/studio.js:88] |\n| 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] |\n| A creator's words are never reused in our version of a viral video | Writer rule | [src: memory brew-viral-remakes] |\n| Our version of a rival ad is a written breakdown only | Product rule since 17 Oct | [src: memory brew-go-live-2026-10-17] |\n| No adult, quarantined or replica communities | Community gate | [src: brew/community-gate.js:181] [src: memory brew-community-seat] |\n| No captcha solving | The Ad Library reader stops on a captcha | [src: brew/reader/adlib.js:37] [src: brew/reader/adlib.js:54] |\n| 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] |\n| No writes to real accounts from the demo | `guardFetch` blocks ad, store and email writes and every send | [src: brew/demo-mode.js:43] |\n| Nothing dummy or test on production | Clean gate before every promote | [src: memory prod-clean-only] |\n| No payments, logistics or tax | Out of scope | [src: memory brew-name-and-scope] |\n| A closed store is visible only to its team | Closed-store gate | [src: memory brew-closed-store-gate] |\n| 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] |\n\n### How we explain it\n\nEach message says what happened, whether it cost anything and the next step. Examples on main:\n\n| When | What the person reads | Source |\n|---|---|---|\n| 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] |\n| 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] |\n| 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] |\n| A claim is cut | Why, such as \"the code isn't on your store\" | [src: brew/claims.js:68-81] |\n| A day's budget is used | Which share is used and that it resets at midnight UTC | [src: brew/budget.js:71-77] |\n| 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 |\n|---|---|---|\n| Every refusal says what happened, the cost and the next step | v1 | Stays |\n| Refusal messages with no vendor names and no product name | v1 | Changes |\n| A refusal log per brand, so repeated refusals on one product show | v1 | New |\n| Platform policy checks before anything posts | v2 | New |\n| Ad policy and spend rules before any ad change, inside the brand's own limits | v4 | New |\n| 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.\n2. 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].\n3. For Veo, keep sleep words out of dialogue and use the fast model [src: memory ugc-factory-pipeline].\n4. Read a Sloosh refusal from the log line `[finals] Sloosh refused` or `GET /api/ops/sloosh` [src: memory brew-sloosh-finals].\n5. Never retry a refused prompt in a loop; one plainer retry at most.",
  "Numbers": "| Metric | Definition | Where measured | Status |\n|---|---|---|---|\n| Refusal rate by maker | Refused jobs over jobs, per maker and day | engines and finals logs | Proposed |\n| Charged refusals | Refusals that cost credits or dollars | finals records | Proposed, target 0 |\n| 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.\n2. **Refusal log.** Refusals are kept only in memory on one instance. Proposal: store them per brand and month beside the spend file."
 }
}