---
id: "2.12"
title: "Product rules"
dept: product
status: draft
owner: Param
branded: false
last_verified: 2026-10-26
sources:
  - "brew origin/main b91e364: SPEC.md:11-20, CLAUDE.md:7-45, DUMMY.md:25-30, SCOPE.md:36-38"
  - "brew origin/main: card-plan.js:36, claims.js:219, rival-ad.js:7, connectors/index.js:267, docs/handover/product/04-content-and-accuracy-rules.md"
  - "brand-directions/_shared/product-model.md:97, 162-207"
  - "brand-directions/docs2/content/decisions.mjs:3-23"
  - "brand-directions/BRIEF.md section 1"
  - "memory: feed-for-all-brands, fix-as-product, brew-one-version, brew-proof-plain-make, prod-clean-only, never-disconnect-arata-meta, brew-quiet-ui, no-disclaimers-in-ads, sell-relief-not-discounts, keep-all-moves, brew-access-rules, brew-qa-round2-2026-10-24, brew-card-asset-match, brew-rival-pictures, harry-reviews-fictional"
---

# Product rules

## Answer

These are the rules every Brew feature must follow, each with where it is set and how it is enforced; a change that breaks one does not ship.

## Today

| # | Rule | Where it holds | Source |
|---|---|---|---|
| 1 | Truth first: no invented numbers, quotes, founders, reviews or backstory; unbacked numbers say to confirm | Prompts, `stat-check.js`, `test/honesty.test.js` | [src: SPEC.md:13] [src: DUMMY.md:30] |
| 2 | Observed, inferred and implication stay separate; never state a rival's motive | Prompts | [src: SPEC.md:14-15] |
| 3 | An error is never shown as none or 0 | Readers keep `error` | [src: CLAUDE.md:12] |
| 4 | Read only: nothing posts, sends to customers, pauses or moves budget in v1 | Read only scopes | [src: SCOPE.md:36-38] [src: brand-directions/docs2/content/decisions.mjs:22] |
| 5 | One version, then Redo it; never three options | `MAKE_N` all 1 | [src: card-plan.js:36] [src: memory brew-one-version] |
| 6 | Claims need proof: a health or measured claim needs the store's study or an approved claim | `checkCopy` on every surface | [src: claims.js:219] [src: memory brew-qa-round2-2026-10-24] |
| 7 | A Play is never evidence; proof must prove the claim | `evidence.js`, `proof-logic.js` | [src: memory brew-proof-plain-make] |
| 8 | Every fix works for every brand, platform and card, and heals saved data | `test/non-shopify.test.js`, audits | [src: CLAUDE.md:20-40] [src: memory feed-for-all-brands] [src: memory fix-as-product] |
| 9 | Simulated things carry a Dummy tag, a code comment and a DUMMY.md row | `public/dummy.css` | [src: DUMMY.md:3-10] |
| 10 | Nothing dummy or test reaches production, except the Pro sheet, the Marketplaces label and the catalog photo fallback | `db/prod-clean.mjs` clean gate | [src: memory prod-clean-only] |
| 11 | Never use a rival's picture as ours; Make our version gets only a written breakdown | `rival-ad.js`, `isRivalCard` | [src: rival-ad.js:7] [src: memory brew-rival-pictures] |
| 12 | The card's picture shows what its words promise | `cardProduct`, `cardScene` | [src: memory brew-card-asset-match] |
| 13 | No disclaimers or AI labels on ad creative | Prompts | [src: SPEC.md:18] [src: memory no-disclaimers-in-ads] |
| 14 | Plain, one glance language; quiet pages with no zero tiles | `plain.js`, quiet rules | [src: SPEC.md:19] [src: memory brew-quiet-ui] |
| 15 | Sell relief, never discounts, in badges and nudges | Copy | [src: docs/handover/product/04-content-and-accuracy-rules.md:24] [src: memory sell-relief-not-discounts] |
| 16 | They or them for anyone whose pronouns are unknown | Prompts | [src: SPEC.md:20] |
| 17 | Cost cuts never drop Plays; make each cheaper | Build | [src: memory keep-all-moves] |
| 18 | Brand data is the brand's: only its own email, an invite or staff open it | `auth.js`, `closed-gate.js` | [src: memory brew-access-rules] |
| 19 | Connected numbers stay members only and beat the site read while fresh | `facts-desk.js` | [src: memory brew-connected-facts] |
| 20 | Never disconnect Arata's Meta or Shopify | `PROTECTED_CONNECTIONS` | [src: connectors/index.js:267] [src: memory never-disconnect-arata-meta] |
| 21 | Say Plays, never moves, wherever a person reads | `brew-name.js`, app labels | [src: brand-directions/docs2/content/decisions.mjs:4] |
| 22 | No fake reviews or testimonials, ever | Review styles quote real reviews word for word | [src: memory harry-reviews-fictional] [src: memory brew-jam-v2] |
| 23 | Sample labels on every invented example in docs and design | Docs check | [src: brand-directions/BRIEF.md section 1] |
| 24 | Never show vendor or model names to a founder; a price only on the confirm sheet | Redesign rule | [src: brand-directions/_shared/product-model.md:97] |

## Tomorrow

| Rule | Version | Tag |
|---|---|---|
| The glance rule: closed card of five parts, 25 words at most | v1 | New [src: brand-directions/_shared/product-model.md:166-178] |
| One of each thing: one sheet, one toast, one empty state, one way into each thing | v1 | New |
| Every post or schedule waits for a yes, with undo | v2 | New |
| Results always show their method and source | v3 | New |
| Nothing runs outside rules the founder set | v4 | New |

## How we do it

1. Before shipping, check the change against this table.
2. For a new class of break, add a test or audit and a heal.
3. Run the suite and the relevant audit:
   ```
   AI_MOCK=1 node --test test/*.test.js
   node tools/audit-card-fit.mjs DIR
   ```
4. A rule changes only by Param's call, logged in the decisions log.

## Numbers

| Metric | Definition | Where measured | Status |
|---|---|---|---|
| Rule breaks found | Findings per QA round that break a rule here | QA reports | Proposed |
| Clean gate refusals | Promotions refused by the clean gate | `tools/promote.sh` output | Proposed |

## Open

1. **AGENTS.md lets the handbook name the company behind our team, WRITING.md forbids it.** Proposal: follow WRITING.md and say our team, and fix AGENTS.md.
2. **Is the 25 Oct Pro sheet exception still right?** Proposal: drop it at the redesign.
