Answer
Every email, page and in product message the product sends a brand today, with its file and line, and where Fetch may speak in them tomorrow.
Today
Every message the product sends or shows a brand today, read from brew origin/main d6154b3. Production still shows the older wording until the next promote. Placeholders in angle brackets are filled in by the code.
Emails to the brand's team
| # | Message | Subject or title, as written | Who gets it, and when | Source |
|---|---|---|---|---|
| 1 | Someone joined | <name> joined <brand>, button Review their views |
The admins, when a teammate signs in with the company domain | brew/auth.js:126-127 |
| 2 | Invite | <who> invited you to <brand>, button Join <brand>, small print The link works for 14 days. |
The invited person | brew/auth.js:190-191 |
| 3 | Admin handed over | You're now the admin of <brand>, button Open <brand> |
The new admin | brew/auth.js:210 |
| 4 | Admin given or taken | You're now an admin of <brand> or You're no longer an admin of <brand> |
That person, unless they made the change | brew/auth.js:212-213 |
| 5 | Check reminder | <brand> is waiting for you to check what we read, then Still waiting: check what we read for <brand>, button Check what we read |
The starter on the brand's own email and the admins, after 3 hours and after a day | brew/confirm-remind.js:21, 70-77 |
| 6 | Feed reminder | Open your feed to start <brand>’s plays, then Still waiting: open your feed to start <brand>’s plays, button Open my feed |
The same people, while the Plays wait for the first feed open | brew/confirm-remind.js:84-90 |
| 7 | Plays ready | Your first plays for <brand> are ready, <done> of <total> plays for <brand> are ready, All of <brand>’s plays are ready, <n> of <brand>’s plays were updated. Buttons Open the feed and Read the plays. Small print Every number in them has a source. Anything we could not confirm is marked “to confirm”. |
Everyone on the team, minus anyone with emails off, once per kind | brew/build.js:997-1004, 1013-1028 |
| 8 | Weekly digest | Subject built from what is new, such as <n> new plays and <n> updated plays for <brand> this week, or What people said about <brand> this week. Title This week for <brand>. Sections Waiting on you, Plays we rewrote, New this week, Rival ads we spotted, On the shelf this week, What people are saying, Coming up. Footer No more emails like this. |
Everyone on the team, Monday from 9:00 in the brand's time, at least 7 days apart, never when nothing is new | brew/digest.js:10-14, 59-63, 125-152 |
| 9 | Tagged in a jam | <first name> tagged you in “<jam>”, button Open the jam |
The tagged person, right away | brew/jam-mail.js:37 |
| 10 | Asked in a jam | <first name> asked you a question on “<jam>”, button Answer it |
The person asked, right away | brew/jam-mail.js:38 |
| 11 | Given a jam | <first name> gave you “<jam>”, button Open the jam |
The assignee, right away | brew/jam-mail.js:39 |
| 12 | OK asked or given | <first name> needs your OK on “<jam>”, button Review it; <first name> approved “<jam>” |
The person asked, or the person who asked | brew/jam-mail.js:40-41 |
| 13 | New jam messages | <A> and <B> in “<jam>”, button Open the jam |
People in the jam, batched, at most every 15 minutes, never their own lines or a jam they have open | brew/jam-mail.js:5-6, 74-78 |
| 14 | Jam email footer | You’re getting this because you’re in this jam. with Mute this jam, Only when I’m tagged, No emails from us |
Every jam email | brew/jam-mail.js:30 |
| 15 | Brief status | Your brief “<jam>” is received, The brief “<jam>” is delivered, Our team needs something for “<jam>”, buttons Open the jam or Answer in the jam |
The sender; delivered and needs info also go to everyone in the jam | brew/jam-mail.js:136-148 |
| 16 | Sign in running out | <brand>: the <tool> sign-in runs out on <day> and <brand>: the <tool> sign-in ran out, button Connect <tool> again |
The admins, 7 days ahead and when it runs out | brew/connectors/index.js:489-493 |
All of these come from Brew <hello@send.joinsquareup.com> through Resend, the name read from PRODUCT_NAME, and carry it in the header and the footer line Sent by Brew. The demo site sends none. brew/auth.js:175-177; brew/mail-layout.js:55, 68; brew/product-name.js:6; brew/jam-mail.js:8
Emails to our own inbox
| # | Message | Subject, as written | Source |
|---|---|---|---|
| 17 | Our team tagged in a jam | <brand>: <first name> tagged our team on “<jam>”, at most once an hour per jam |
brew/jam-mail.js:101-111; brew/feed-service.js:2238 |
| 18 | A brief sent to our team | BR-<n> · New brief from <brand>: “<jam>” |
brew/jam-mail.js:113-134 |
| 19 | Product alerts | Brew alert (<site>): <title>, such as Vertex is refusing calls on billing or Brew’s background worker is down, once a day per issue |
brew/alerts.js:29-37, 71 |
Slack
| # | Message | Source |
|---|---|---|
| 20 | Weekly post: *This week in Brew for <brand>*, the name read from PRODUCT_NAME, with the shared lines of the digest and Open the feed |
brew/digest.js:155-167; brew/product-name.js:6 |
| 21 | A jam posted to a channel: Posted to Slack (<channel>) with the full context and an Approve button. Replies in the thread land here and in the brief. |
brew/jam-brief.js:347 |
Pages the server shows
| # | Page | Words, as written | Source |
|---|---|---|---|
| 22 | Email settings | Muted, Only when you’re tagged, No emails from us, Jam emails are on, Nothing changed, This link has expired |
brew/jam-mail.js:92-98 |
| 23 | Invites | This invite has expired, This invite is no longer valid, Access is by invite now |
brew/auth.js:263, 267, 271 |
| 24 | Store not open yet | This store isn’t open yet, It’s still behind Shopify’s password page, so we show it to the store’s own team until it opens. |
brew/closed-gate.js:31-32 |
| 25 | Privacy, Terms, not found | Preview policy · 19 October 2026, We couldn’t find that page |
brew/legal-pages.js:7, 19-51 |
| 26 | Shopify links | Link expired, Link used |
brew/shopify-app.js:252-253 |
In the product
| # | Where | Words, as written | Source |
|---|---|---|---|
| 27 | Landing | Your store’s marketing team, in one feed. |
brew/public/app.js:406 |
| 28 | Outsider wall | <brand> is for <brand>’s team, Just looking around? The demo stores show it all end to end, with nothing to set up. |
brew/public/app.js:287-289 |
| 29 | Demo mode sheet | You’re seeing <brand> in demo mode, We make new work for <brand>’s team. |
brew/public/app.js:90 |
| 30 | A read refused | We read a brand for its own team. Sign in with your work email at <host> to read it. |
brew/server.js:762 |
| 31 | Not a website | That doesn’t look like a website. Try yourbrand.com |
brew/build.js:216 |
| 32 | Read limits | One new brand an hour from the same network. Open one you already started, or try again later., Today’s reading budget is used up. Try again tomorrow. |
brew/build.js:227-228 |
| 33 | Check card | Check what we read for <brand>, The brand brief and the plays are written from what you confirm. |
brew/public/app.js:1633 |
| 34 | Plays paused | The plays are paused, <n> of <N> plays in your plan are written. |
brew/public/app.js:1615 |
| 35 | Writing paused | Our writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back. |
brew/public/app.js:2967 |
| 36 | Sign in failed | Google sign-in did not finish. Try again. |
brew/public/app.js:446 |
| 37 | Feed teaching | Hidden. <agent> will learn from it., Thanks. We’ll learn from it. |
brew/public/app.js:1931, 2539 |
| 38 | Link copied | Link copied. It opens this exactly as it is now. |
brew/public/app.js:2088 |
| 39 | Brief to our team | Brief <BR-n> sent to our team: the brief, deliverables, assets, rules and when it is done. |
brew/jam-brief.js:396 |
| 40 | Brief to own team | Brief <BR-n> copied for your team: the card, options, plan, assets and both chats. Paste it where they work. |
brew/jam-brief.js:399 |
| 41 | Gmail draft | Gmail draft opened. and That didn’t go through. Try again. |
brew/public/app.js:4603 |
| 42 | Final making | Making the final <kind>. It takes a few minutes; we’ll notify you when it’s ready. |
brew/public/app.js:4062 |
| 43 | Connect errors | Connect again: the sign-in expired., Too many tries. Wait a few minutes, then paste it again. |
brew/connectors/index.js:412, 435 |
| 44 | Daily budget | Today’s budget is used up. Try again tomorrow. |
brew/build.js:1103 |
| 45 | Delete my data | This sends a deletion request to our team from your own email. We delete your data within 7 days and reply when it’s done. |
brew/public/live.js:348-349 |
| 46 | Brief status refused | Only our team moves a brief on., Say what you need from them. |
brew/jam-brief.js:408; brew/brief-status.js:26 |
What the list shows:
- Customer copy says we or our team on origin/main, and the ShopOS team is gone. The product name is left only where a name is needed, such as the sender, the email header and footer, and the Slack post, all from one constant that still says Brew. brew/product-name.js:1-6; brew/mail-layout.js:55, 68; brew/digest.js:167; brew commit ca2bc7b
- Every email says plays on origin/main, the digest and the feed reminder included. brew/digest.js:131, 137; brew/confirm-remind.js:84; brew/build.js:1013
test/product-name.test.jsfails a customer template that says ShopOS or moves, or names the product other than fromproduct-name.js. brew/test/product-name.test.js:1-16- No character speaks in any message today.
Tomorrow
Changes for every direction
| Today | Tomorrow | Version and tag |
|---|---|---|
<name> joined <brand> on Brew |
<name> joined <brand>, done on origin/main |
v1, Stays |
<who> invited you to <brand> on Brew |
<who> invited you to <brand>, done on origin/main |
v1, Stays |
check what Brew read |
check what we read, done on origin/main |
v1, Stays |
Open your feed to start <brand>’s moves |
Open your feed to start <brand>’s plays, done on origin/main |
v1, Stays |
Moves Brew rewrote, new moves |
Plays we rewrote, new plays, done on origin/main |
v1, Stays |
No emails from Brew |
No emails from us, done on origin/main |
v1, Stays |
The ShopOS team needs something for |
Our team needs something for, done on origin/main |
v1, Stays |
Brew <hello@send.joinsquareup.com>, from PRODUCT_NAME |
The picked name, changed in the one constant | v1, Changes |
| No character in any message | One line from the character in the places below, never in the rest | v1, New |
Where Fetch speaks
The plain line always stays. Fetch's line sits beside it, never instead of it.
| Message | Plain line | Fetch's line | Version and tag |
|---|---|---|---|
| Plays ready email | Subject and body stay plain | Sign off: “Fetched for you. Over to you now.” | v1, New |
| Weekly digest | Subject stays plain | First line, once the Monday drop ships: “Monday. Five picks, dropped at your feet.” | v1, New |
| Reading screen | The plain step names | “Sniffing round your store. About 3 to 4 minutes.” The time must match a measured read. | v1, New |
| A pick hearted | You’ll see more like this |
“Good pick. Fetching more like it.” | v1, New |
| A new version | The version number | “Another throw. The first one stays in the pile.” | v1, New |
| Needs your yes | The question, plain | “Only you can answer these. I will sit here.” | v1, New |
| Brief copied for the own team | Brief <BR-n> copied for your team |
“Brief copied. Paste it to them, nothing was sent.” | v1, New |
| Caught up, end of the feed | Plain line on what comes next | “That is the week. Every pick seen.” | v1, New |
| An action that failed and can be retried | What happened, what is safe, one button | “Lost the scent on that one. Your work is safe. Try again.” | v1, New |
| A number to confirm | The source chip | “I am not sure of this number. Confirm it before you use it.” | v1, New |
| Read cap reached | Today’s reading budget is used up. Try again tomorrow. |
“Out of throws for now. Reads are capped while we build v0. Ask us for one.” | v1, New |
| Check and feed reminders | Plain | None. A reminder asks for a person's time. | v1, Stays |
| Jam emails | Plain, the teammate's words lead | None | v1, Stays |
| Invites and admin changes | Plain | None. Access stays plain. | v1, Stays |
| Sign in running out | Plain | None. Access stays plain. | v1, Stays |
| Brief status from our team | Plain, signed by a person | None. A person writes it. | v1, Stays |
| Writing paused, outages | Plain, from a person | None. An error on our side. | v1, Stays |
| Deletion, privacy, terms | Plain | None. Privacy stays plain. | v1, Stays |
Fetch's rules for these lines
- Fact first, joke second. One joke per line at most.
- Never more than two sentences in the product.
- Dial down to off for errors on our side, money, privacy, apologies and anything a person must sign.
brand-directions/docs2/data/dirs.json; brand-directions/dog/system.md
Lines Fetch never says, in any of these:
- “I posted it for you.”
- “Scheduled for 9am.”
- “Your sales went up after my picks.”
- “You missed a walk. I waited all day.”
- “Bad owner.”
brand-directions/docs2/data/dirs.json
Words Fetch may use around the controls: “Drop it” for Hand it off, “Chew on it” for Make it, “Another throw” for New version, “Leave it” for Not for me, “Nap time” for You're caught up. brand-directions/docs2/data/dirs.json
How we do it
Before adding or changing any message, find every place it is written:
git -C /Users/param/Documents/ChatGPT/brew grep -n -E "subject|toast\(|smallPage|page\(" origin/main -- '*.js' ':(exclude)test/*'Write the plain line first: what happened, what is safe, one way out. No product name, no company name, Plays never moves.
Add Fetch's line only where the table above allows it, and only from the approved lines or the words table.
Add the message to this list with its file and line in the same change.
Run the honesty and copy tests, then deploy to staging and read the email in a real inbox before promoting.
cd /Users/param/Documents/ChatGPT/brew && node --test test/honest-copy.test.js test/jam-mail.test.js test/shopos-name.test.js test/product-name.test.js
Numbers
| Metric | Definition | Where measured | State |
|---|---|---|---|
| Messages listed | Rows in this list against strings found by the grep above | This document | Proposed |
| Emails off | People per brand who set no emails | private/mail-prefs.json mail off |
Proposed |
| Name leaks | Customer strings that name the product or our company | the grep above | Proposed |
| Weekly digests sent | Digests that went out per brand per week | private/digest.json |
Proposed |
Open
- Our company name is gone and the product name sits in one constant, still Brew. Do we keep Brew in the sender, header and Slack post until the name is picked? Proposal: yes, then change the one line.
- May Fetch speak in emails at all, or only in the product? Proposal: only in the sign off of the Plays email and the first line of the digest, as in the table.
- The read lines promise 3 to 4 minutes. Is that true? Proposal: measure ten first reads on production and use the measured time, or drop the number.
- Should the sender be a person, such as Param at our team, rather than the product? Proposal: the product sends routine emails; anything personal comes from a named person.