Snapshots

What changed in 1.1

Against 1.0: 60 documents.

diff --git a/content/brand/naming.md b/content/brand/naming.md
index 7894785..b5d29b6 100644
--- a/content/brand/naming.md
+++ b/content/brand/naming.md
@@ -8 +8 @@ branded: false
-last_verified: '2026-10-26'
+last_verified: "2026-10-26"
@@ -14 +14 @@ sources:
-  - "brew origin/main a559de6: public/index.html title, word counts in public/app.js, public/live.js, public/brand-book.js"
+  - "brew origin/main d6154b3: public/index.html title, word counts in public/app.js, public/live.js, public/brand-book.js, product-name.js:1-9, docs/slack-app-manifest.json:4, 10, docs/shopify.app.toml:8, docs/shopify-app-review.md:107; commit ca2bc7b"
@@ -34 +34,3 @@ The product is being renamed and has no new name yet; this document holds the te
-| The word Brew appears 261 times in public/app.js, 44 in public/live.js and 30 in public/brand-book.js, comments included | Counted on origin/main a559de6; to confirm how many are visible [src: brew/public/app.js; brew/public/live.js; brew/public/brand-book.js] |
+| The word Brew appears 87 times in public/app.js, 7 in public/live.js and 5 in public/brand-book.js, comments included, down from 261, 44 and 30 on a559de6 | Counted as whole words on origin/main d6154b3, after the 26 Oct copy pass; to confirm how many are visible [src: brew/public/app.js; brew/public/live.js; brew/public/brand-book.js; brew commit ca2bc7b] |
+| Since 26 Oct the name customers read comes from one constant, `PRODUCT_NAME` in `product-name.js`, still "Brew"; copy that can say we or our team does, so the rename is one line. Code names such as routes, keys, cookies and `shopos-feed` stay | [src: brew/product-name.js:1-9] |
+| Left with the name on purpose: the Slack app name and bot, `docs/shopify.app.toml`, the Shopify listing draft `Brew: Daily Marketing Plays`, `manifest.webmanifest` | [src: brew/docs/slack-app-manifest.json:4, 10; brew/docs/shopify.app.toml:8; brew/docs/shopify-app-review.md:107; brew commit ca2bc7b] |
@@ -60 +62 @@ The product is being renamed and has no new name yet; this document holds the te
-7. Fill every wordmark slot, then rename the visible strings. List them with:
+7. Fill every wordmark slot, then change `PRODUCT_NAME` in `brew/product-name.js`, which renames every email, page, notification, Slack post and PDF that reads it. Then rename the strings left on purpose, the Slack app, the Shopify app and listing and `manifest.webmanifest`, and list any other visible string with:
diff --git a/content/company-and-strategy/open-questions.md b/content/company-and-strategy/open-questions.md
index be6ca4b..7fe8107 100644
--- a/content/company-and-strategy/open-questions.md
+++ b/content/company-and-strategy/open-questions.md
@@ -17 +17 @@ sources:
-Every open call across the handbook in one list: 675 today.
+Every open call across the handbook in one list: 672 today.
@@ -213 +213 @@ Every open call across the handbook in one list: 675 today.
-| 5.12 | Testing and QA | **Do we add CI now?** Proposal: yes, a GitHub Action on push to main that runs the suite; it does not deploy. |
+| 5.12 | Testing and QA | **Should a red CI run block a deploy?** Proposal: yes, `deploy.sh` checks the commit's Actions status first and stops on red. |
@@ -222 +222 @@ Every open call across the handbook in one list: 675 today.
-| 5.16 | Tech debt | **Which High item first?** Proposal: D-14 (CI) this week, it is a day's work and guards everything else; then D-1. |
+| 5.16 | Tech debt | **Which High item first, now that CI runs?** Proposal: D-1 this week, then D-3. |
@@ -276 +276 @@ Every open call across the handbook in one list: 675 today.
-| 8.1 | Runbook index | **Should the three bucket scripts be deleted now?** Proposal: keep them, add a first line that stops when the service runs `STORAGE=pg`, and write the Postgres versions next. |
+| 8.1 | Runbook index | **Should the bucket scripts be deleted now?** `tools/reset-brand.sh` and `tools/flush-live.sh` already stop on `STORAGE=pg`. Proposal: give `tools/reset-jams.sh` the same first lines, and delete all three once production has run on Postgres for a month. |
@@ -282,2 +282,2 @@ Every open call across the handbook in one list: 675 today.
-| 8.4 | Wipe data and reset jams | **Build the Postgres brand wipe now?** Proposal: yes, as `db/reset-brand.mjs` run by `brew-ops`, copying the jam reset's archive, marker and protected-path guard, and refusing the five partners unless a `--partner` word is given. |
-| 8.4 | Wipe data and reset jams | **May anyone ever wipe on production?** Proposal: no wipe tool for production; a partner's "Delete my data" request is done by Param by hand with a backup first, until v2. |
+| 8.4 | Wipe data and reset jams | **May a brand reset run on production?** The code allows it, with no archive. Proposal: no; production runs only `db/delete-brand.mjs` for a data request. |
+| 8.4 | Wipe data and reset jams | **What clears the cache and made media on Postgres?** Proposal: add it to `db/reset-brand.mjs` behind a `--with-cache` flag, so "wipe data" is one job. |
@@ -324 +323,0 @@ Every open call across the handbook in one list: 675 today.
-| 9.5 | Retention and deletion | **Build the production deletion job now?** Proposal: yes, before the first brand outside the five partners, with a dry run first. |
@@ -369 +368 @@ Every open call across the handbook in one list: 675 today.
-| 10.4 | Website | **Do the landing's sample cards stay?** Proposal: label them Sample this week. |
+| 10.4 | Website | **Do the landing's Sample cards stay, now that they are labelled?** Proposal: keep them until a partner lets us show a real finding. |
@@ -450 +448,0 @@ Every open call across the handbook in one list: 675 today.
-| 11.8 | Demo script | **Who fixes the picker hint?** Proposal: change it to "Reads the store live, you check what it found, the Plays are ready. About 4 minutes." in the next release. |
@@ -467 +465 @@ Every open call across the handbook in one list: 675 today.
-| 12.2 | Help centre | Is the 7 day deletion promise ours to keep on production? Proposal: yes, once the production deletion job exists; until then an engineer does it by hand within 7 days. |
+| 12.2 | Help centre | Is the 7 day deletion promise ours to keep on production? Proposal: yes. The deletion tool, `db/delete-brand.mjs`, is on origin/main and reaches production with the next promote; until then an engineer does it by hand within 7 days. |
@@ -476 +474 @@ Every open call across the handbook in one list: 675 today.
-| 12.4 | Known issues | The interface still says Brew. Do we filter the name out of the product before the five partners use it daily? Proposal: yes, a display filter in v1 that says we, as the jam RCA listed. |
+| 12.4 | Known issues | The interface still says Brew in a few places, now from one constant. Do we set that constant to the picked name, or to none, before the five partners use it daily? Proposal: keep Brew until the name is picked, then change the one line. |
@@ -491 +488,0 @@ Every open call across the handbook in one list: 675 today.
-| 12.8 | Briefs sent to our team | Do we build the staff status buttons now? Proposal: yes, in v1, before any partner sends a second brief. |
@@ -493,2 +490,2 @@ Every open call across the handbook in one list: 675 today.
-| 12.9 | Data requests | Do we build the production deletion job now? Proposal: yes, before any partner asks, as `db/delete-brand.mjs` run as a Cloud Run job with a dry run first. |
-| 12.9 | Data requests | How long do we keep the archive after a deletion? Proposal: 30 days, then purge, and say so on the privacy page. |
+| 12.9 | Data requests | When do we make the `brew-prod-ops` job? Proposal: right after the next promote, with one dry run on a test brand on staging first. |
+| 12.9 | Data requests | The tool keeps no archive, only the audit row and the backups for up to 14 days. Do we say that on the privacy page? Proposal: yes, one line with the 14 days. |
@@ -497 +494 @@ Every open call across the handbook in one list: 675 today.
-| 12.10 | Every message | Do we strip the product name and our company name from every customer message now, before the name is picked? Proposal: yes, in v1, with the wordmark slot where the name would go. |
+| 12.10 | Every message | 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. |
@@ -671 +668 @@ Every open call across the handbook in one list: 675 today.
-| 18.1 | Shopify app | **The listing name is `Brew: Daily Marketing Moves`.** The product now says Plays and the name is open. Proposal: keep the listing as submitted until approval, then rename to the picked name with Plays in the subtitle. |
+| 18.1 | Shopify app | **The repo draft now says `Brew: Daily Marketing Plays`; the listing was submitted as `Brew: Daily Marketing Moves`.** Proposal: change the live listing to the Plays wording only after approval, so the review is not reset, then rename to the picked name. |
@@ -684 +681 @@ Every open call across the handbook in one list: 675 today.
-| 18.4 | Slack | **The manifest says moves and "Brew by SquareUp".** Proposal: rewrite the description with Plays and no product name once the name is picked, in one manifest update. |
+| 18.4 | Slack | **The manifest now says plays, and still names the app Brew.** Proposal: push the new description with `tools/slack-app.mjs update` now, and rename the app in one more update once the name is picked. |
diff --git a/content/customer-success-and-support/briefs-sent-to-our-team.md b/content/customer-success-and-support/briefs-sent-to-our-team.md
index b734c24..071666d 100644
--- a/content/customer-success-and-support/briefs-sent-to-our-team.md
+++ b/content/customer-success-and-support/briefs-sent-to-our-team.md
@@ -11 +11,4 @@ sources:
-  - "brew/jam-brief.js:360-415"
+  - "brew/jam-brief.js:361-416"
+  - "brew/public/brief-staff.js:1-52"
+  - "brew/public/jam-room.js:164, 588"
+  - "brew/test/brief-staff.test.js:31-94"
@@ -13 +16 @@ sources:
-  - "brew/feed-service.js:350-362, 376"
+  - "brew/feed-service.js:353-362, 376-384"
@@ -15 +18,2 @@ sources:
-  - "brew/public/app.js:1447, 2891-2893, 4585-4589"
+  - "brew/public/app.js:1448, 2892-2894, 4150-4151, 4585-4590, 5948"
+  - "brew commits 2eed121, d6154b3"
@@ -31,7 +35,7 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-- A jam ends with Share the brief. To the brand's own team the brief is copied with a link to the full read only brief, and nothing is sent. To our team it is sent by email and tracked. [src: brew/jam-brief.js:360-363; brew/SCOPE.md:20]
-- The team brief's Send to sheet lists who it is for: your team, your agency, a designer, a media buyer, a creator, a video editor, or our team. [src: brew/team-brief.js:17-21; brew/public/app.js:4585-4589]
-- Picking our team shows one line and one button: we get the brief and the files by email, make it and run it, and the brand sees where it stands in the top bar. [src: brew/public/app.js:4587]
-- Anything still missing from the brief, and anything the checks still flag, is asked once before it goes. It never blocks. [src: brew/jam-brief.js:369-372]
-- Each sent brief gets a ticket, BR followed by a number from 1042 up. [src: brew/feed-service.js:376]
-- The same brief sent twice within ten minutes is not emailed twice. [src: brew/jam-brief.js:378-380]
-- The demo site never emails a brief. If the email fails, the brief is not marked sent and stays ready to paste. [src: brew/jam-brief.js:391-393; brew/feed-service.js:356-359]
+- A jam ends with Share the brief. To the brand's own team the brief is copied with a link to the full read only brief, and nothing is sent. To our team it is sent by email and tracked. [src: brew/jam-brief.js:361-363; brew/SCOPE.md:20]
+- The team brief's Send to sheet lists who it is for: your team, your agency, a designer, a media buyer, a creator, a video editor, or our team. [src: brew/team-brief.js:20; brew/public/app.js:4585-4590]
+- Picking our team shows one line and one button, Send to our team: our team gets the brief and the files by email, makes it and runs it, and the brand sees where it stands in the top bar. [src: brew/public/app.js:4590]
+- Anything still missing from the brief, and anything the checks still flag, is asked once before it goes. It never blocks. [src: brew/jam-brief.js:370-373]
+- Each sent brief gets a ticket, BR followed by a number from 1042 up. [src: brew/feed-service.js:376-384]
+- The same brief sent twice within ten minutes is not emailed twice. [src: brew/jam-brief.js:379-381]
+- The demo site never emails a brief. If the email fails, the brief is not marked sent and stays ready to paste. [src: brew/jam-brief.js:392-394; brew/feed-service.js:356-359]
@@ -41,3 +45,3 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-- One email per send to the inbox in `SHOPOS_EMAIL`, hello@joinsquareup.com by default. [src: brew/jam-mail.js:112-117]
-- It carries who sent it, where it runs, the deliverables, the brief itself, the assets with images inline, how a reference is made, the rules and when it is done, plus buttons to open the jam and read the brief. [src: brew/jam-mail.js:119-132]
-- Staff sign in with their own staff Google account to open the jam. [src: brew/jam-mail.js:107]
+- One email per send to the inbox in `SHOPOS_EMAIL`, hello@joinsquareup.com by default. [src: brew/jam-mail.js:113-118]
+- It carries who sent it, where it runs, the deliverables, the brief itself, the assets with images inline, how a reference is made, the rules and when it is done, plus buttons to open the jam and read the brief. [src: brew/jam-mail.js:120-133]
+- Staff sign in with their own staff Google account to open the jam. [src: brew/jam-mail.js:102]
@@ -49 +53 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-| sent | Sent | received, in progress, needs info, delivered | [src: brew/brief-status.js:8-11] |
+| sent | Sent | received, in progress, needs info, delivered | [src: brew/brief-status.js:8-12] |
@@ -55,2 +59,2 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-- Only staff move a brief on, through `POST /api/work/:wid/brief/status`. Guests cannot. [src: brew/jam-brief.js:405-407; brew/docs/handover/engineering/12-ui2-service-api.md:18, 30]
-- Needs info must say what we need. A status with a note folds into the brief; a bare status does not change it. [src: brew/brief-status.js:26; brew/jam-brief.js:410-411]
+- Only staff move a brief on, through `POST /api/work/:wid/brief/status`. Anyone else gets a 403 that says "Only our team moves a brief on." [src: brew/jam-brief.js:406-408; brew/docs/handover/engineering/12-ui2-service-api.md:18, 30]
+- Needs info must say what we need. A status with a note folds into the brief; a bare status does not change it. [src: brew/brief-status.js:26; brew/jam-brief.js:411-412]
@@ -58,2 +62,16 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-- The top bar shows a pill with how many briefs are with our team, and flags any that need info. [src: brew/public/app.js:1447, 2891-2893; brew/brief-status.js:32-37]
-- There is no button for staff to move a status. The email says to move it on from the jam, but only the route exists. [src: brew/jam-mail.js:129]
+- The top bar shows a pill with how many briefs are with our team, and flags any that need info. [src: brew/public/app.js:1448, 2892-2894; brew/brief-status.js:32-37]
+- Staff now have status controls in the jam, in the studio's ship bar and the jam room's ship row. They show the ticket and where the brief is, one main button for the usual next step and the other allowed steps in a small menu. [src: brew/public/brief-staff.js:1-31; brew/public/app.js:4150-4151; brew/public/jam-room.js:164]
+
+  | Status now | Main button | In the menu | Source |
+  |---|---|---|---|
+  | Sent | Mark received | Start work, Ask for info, Mark delivered | [src: brew/public/brief-staff.js:7-11] |
+  | Received | Start work | Ask for info, Mark delivered | [src: brew/public/brief-staff.js:7-11] |
+  | In progress | Mark delivered | Ask for info | [src: brew/public/brief-staff.js:7-11] |
+  | Needs info | Back to work | Mark delivered | [src: brew/public/brief-staff.js:7-11] |
+  | Delivered | none; a quiet Reopen it button | | [src: brew/public/brief-staff.js:8-9, 31] |
+
+- Ask for info opens a box first, "What do you need from them?", and Send the question sends it as the note. [src: brew/public/brief-staff.js:25-27, 37]
+- After a change the work is refreshed, so the brand's top bar pill follows, and a toast says the brand sees it now. [src: brew/public/brief-staff.js:39-43]
+- Only our own people see the controls (`LIVE.full`). Brand members, guests and the demo site never do, and a brief copied for the brand's own team has none. The route checks staff again. [src: brew/public/brief-staff.js:2, 13-16; brew/test/brief-staff.test.js:60]
+- Tests keep the client's steps the same as the server's, and refuse a brand member every status. [src: brew/test/brief-staff.test.js:31, 40, 52]
+- The controls are on brew origin/main and going to staging now. Production has not been promoted with them yet. [src: brew commits 2eed121, d6154b3; deploy state 26 Oct]
@@ -65 +83 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-| v1 | New | Status buttons for staff in the jam: Received, In progress, Needs info with a note, Delivered. |
+| v1 | Stays | Status buttons for staff in the jam, built on origin/main: Mark received, Start work, Ask for info with a question, Mark delivered, Reopen it. Reach production with the next promote. |
@@ -68 +86 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-| v1 | Changes | The customer emails say our team, never ShopOS, and never the product name. |
+| v1 | Stays | The brief emails and refusals say our team, never ShopOS. Done on origin/main. |
@@ -76 +94 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-3. Move it to Received the same working day. Until the buttons exist, an engineer calls the route:
+3. Move it to Received the same working day with Mark received in the jam's ship bar. On a site not yet promoted with the buttons, an engineer calls the route:
@@ -84,3 +102,3 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-4. If anything is missing, move it to Needs info with one plain question in the note. The brand answers in the jam.
-5. Move it to In progress when work starts, and Delivered when the files are in the jam.
-6. If the brand asks for a change after delivery, move it back to In progress.
+4. If anything is missing, press Ask for info and type one plain question. The brand answers in the jam.
+5. Press Start work when work starts, and Mark delivered when the files are in the jam.
+6. If the brand asks for a change after delivery, press Reopen it.
@@ -102,2 +120 @@ How a brief a brand sends to our team is received, tracked and delivered, and wh
-3. Do we build the staff status buttons now? Proposal: yes, in v1, before any partner sends a second brief.
-4. Do briefs to our team cost money during the design partnership? Proposal: free until pricing is unparked, then paid per brief as the 26 Oct pricing note says.
+3. Do briefs to our team cost money during the design partnership? Proposal: free until pricing is unparked, then paid per brief as the 26 Oct pricing note says.
diff --git a/content/customer-success-and-support/data-requests.md b/content/customer-success-and-support/data-requests.md
index ebebc48..0a0eb3d 100644
--- a/content/customer-success-and-support/data-requests.md
+++ b/content/customer-success-and-support/data-requests.md
@@ -11,2 +11,2 @@ sources:
-  - "brew/legal-pages.js:18-33"
-  - "brew/shopify-app.js:15-19, 30, 203-217"
+  - "brew/legal-pages.js:26, 33-34"
+  - "brew/shopify-app.js:18-19, 31, 204-218"
@@ -14 +14,7 @@ sources:
-  - "brew/tools/reset-brand.sh:1-13"
+  - "brew/tools/reset-brand.sh:11-19"
+  - "brew/db/brand-wipe.js:1-9, 24-32, 74-84, 236-240"
+  - "brew/db/delete-brand.mjs:1-10"
+  - "brew/db/wipe-cli.js:9-13"
+  - "brew/connectors/protected.js:3"
+  - "brew/db/migrations/010_data_deletions.sql:1-18"
+  - "brew/docs/production.md:66-110"
@@ -17 +23,2 @@ sources:
-  - "brew/public/app.js:2954"
+  - "brew/public/app.js:2955"
+  - "brew commits e3e0fb4, d6154b3"
@@ -33 +40 @@ How a brand or a person asks us to export or delete their data, what we promise,
-- The privacy page asks for an email from the work address naming the store, and says we delete the read, Plays, jams and connected keys and reply when done. It gives no time. [src: brew/legal-pages.js:32-33]
+- The privacy page asks for an email from the work address naming the store, and says we delete the read, Plays, jams and connected keys and reply when done. It gives no time. [src: brew/legal-pages.js:33-34]
@@ -35 +42 @@ How a brand or a person asks us to export or delete their data, what we promise,
-- Start again in the account menu only clears what this browser kept. It deletes nothing on our side. [src: brew/public/app.js:2954]
+- Start again in the account menu only clears what this browser kept. It deletes nothing on our side. [src: brew/public/app.js:2955]
@@ -39,3 +46,3 @@ How a brand or a person asks us to export or delete their data, what we promise,
-- Uninstalling stops every read and removes our access to the store. [src: brew/legal-pages.js:25]
-- `shop/redact`, which Shopify sends 48 hours after an uninstall, drops the store's token and numbers, even on a protected brand, unless the store installed again since. [src: brew/shopify-app.js:30, 203-214]
-- `customers/data_request` and `customers/redact` have nothing to send or erase, because we keep no record of any one customer. [src: brew/shopify-app.js:15-19, 217]
+- Uninstalling stops every read and removes our access to the store. [src: brew/legal-pages.js:26]
+- `shop/redact`, which Shopify sends 48 hours after an uninstall, drops the store's token and numbers, even on a protected brand, unless the store installed again since. [src: brew/shopify-app.js:31, 204-216]
+- `customers/data_request` and `customers/redact` have nothing to send or erase, because we keep no record of any one customer. [src: brew/shopify-app.js:18-19, 218]
@@ -49,2 +56,8 @@ How a brand or a person asks us to export or delete their data, what we promise,
-- `tools/reset-brand.sh` wipes a brand from the staging bucket and archives a copy. It does not touch production or Postgres. [src: brew/tools/reset-brand.sh:1-13]
-- Production runs on Cloud SQL and the gs://brew-prod bucket since 25 Oct. No deletion job exists for it. [src: memory brew-prod-backend, brew-storage]
+- `db/delete-brand.mjs` deletes all of one brand's data for a data request, on a site that keeps its data in Postgres. It is a dry run until `--yes`, and prints exactly what would go. [src: brew/db/delete-brand.mjs:1-4]
+- It removes every row under `brands/<slug>/` with our stored tokens and connections, the workspaces, archived copies in `_deleted/`, build progress, jobs, bus messages, counters, access log rows, every bucket version of its files, and made or research media no other row points at. [src: brew/db/delete-brand.mjs:5-7; brew/db/brand-wipe.js:5-6]
+- It keeps one `data_deletions` row: counts by folder, when, who, the grants, and what backups still hold. Never contents or member names. [src: brew/db/migrations/010_data_deletions.sql:1-3; brew/db/brand-wipe.js:132-133, 187-195]
+- It never calls a platform. It prints the grants the brand must revoke itself, and where. [src: brew/db/brand-wipe.js:7, 74-84, 240]
+- It refuses the five design partners unless `--partner-override --confirm-slug <slug>`, and always refuses a brand in `PROTECTED_CONNECTIONS`, Arata by default. [src: brew/db/brand-wipe.js:24-32; brew/connectors/protected.js:3]
+- What still holds a deleted brand for a while: point in time logs for 7 days, 14 daily backups, the nightly export, and the bucket's 7 day soft delete. [src: brew/db/brand-wipe.js:236-238; brew/docs/production.md:108-110]
+- Staging runs it as the `brew-ops` job. Production needs a `brew-prod-ops` job made once from the promoted image, and the `data_deletions` table arrives with the next promote. Neither exists on production today. [src: brew/docs/production.md:83-106; deploy state 26 Oct]
+- `tools/reset-brand.sh` is bucket era: with `STORAGE=pg`, the default, it prints where to go and exits 3. [src: brew/tools/reset-brand.sh:11-19]
@@ -59 +72 @@ How a brand or a person asks us to export or delete their data, what we promise,
-| v1 | New | A production deletion job that removes one brand's rows, files, connected keys and workspaces, archives a copy for 30 days, then purges it. Proposed. |
+| v1 | Changes | The deletion job is built on origin/main and works on staging. Production gets it with the next promote and a one time `brew-prod-ops` job. It keeps no archive, only the audit row. |
@@ -71,2 +84 @@ How a brand or a person asks us to export or delete their data, what we promise,
-5. For a whole brand on production: an engineer removes the brand's rows and files by hand until the deletion job exists, keeping an archive. Never touch a partner who did not ask.
-6. For a brand on staging only, run the staging wipe:
+5. For a whole brand, dry run first and read every line. On staging:
@@ -75 +87,2 @@ How a brand or a person asks us to export or delete their data, what we promise,
-   cd /Users/param/Documents/ChatGPT/brew && tools/reset-brand.sh <slug>
+   gcloud run jobs execute brew-ops --region=asia-south1 --args=db/delete-brand.mjs,--slug,<slug> --wait
+   gcloud run jobs execute brew-ops --region=asia-south1 --args=db/delete-brand.mjs,--slug,<slug>,--rev-n,<n>,--yes,--request,<ref>,--operator,<name> --wait
@@ -78 +91,3 @@ How a brand or a person asks us to export or delete their data, what we promise,
-7. Tell them to revoke our access on Shopify, Meta, Google or Slack themselves if they want the grant gone too.
+   On production, once promoted and the `brew-prod-ops` job is made as `docs/production.md` says, the same with `brew-prod-ops` and without `--rev-n`. Never touch a partner who did not ask.
+6. Copy the grants the run prints and what backups still hold into the reply. A `data_deletions` row with `done_at` empty did not finish: run it again.
+7. Tell them to revoke our access on Shopify, Meta, Google, Slack, Klaviyo or the reviews app themselves, as the run printed.
@@ -91,2 +106,2 @@ How a brand or a person asks us to export or delete their data, what we promise,
-1. Do we build the production deletion job now? Proposal: yes, before any partner asks, as `db/delete-brand.mjs` run as a Cloud Run job with a dry run first.
-2. How long do we keep the archive after a deletion? Proposal: 30 days, then purge, and say so on the privacy page.
+1. When do we make the `brew-prod-ops` job? Proposal: right after the next promote, with one dry run on a test brand on staging first.
+2. The tool keeps no archive, only the audit row and the backups for up to 14 days. Do we say that on the privacy page? Proposal: yes, one line with the 14 days.
diff --git a/content/customer-success-and-support/every-message.bee.md b/content/customer-success-and-support/every-message.bee.md
index 802b52f..5f6f024 100644
--- a/content/customer-success-and-support/every-message.bee.md
+++ b/content/customer-success-and-support/every-message.bee.md
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: auth.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
+  - "brew origin/main d6154b3: auth.js, product-name.js, mail-layout.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
@@ -26 +26 @@ Every email, page and in product message the product sends a brand today, with i
-Every message the product sends or shows a brand today, read from brew origin/main a559de6. Placeholders in angle brackets are filled in by the code.
+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.
@@ -32,18 +32,18 @@ Every message the product sends or shows a brand today, read from brew origin/ma
-| 1 | Someone joined | `<name> joined <brand> on Brew`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: brew/auth.js:125-126] |
-| 2 | Invite | `<who> invited you to <brand> on Brew`, button `Join <brand>`, small print `The link works for 14 days.` | The invited person | [src: brew/auth.js:189-190] |
-| 3 | Admin handed over | `You're now the admin of <brand> on Brew`, button `Open <brand>` | The new admin | [src: brew/auth.js:209] |
-| 4 | Admin given or taken | `You're now an admin of <brand> on Brew` or `You're no longer an admin of <brand> on Brew` | That person, unless they made the change | [src: brew/auth.js:211-212] |
-| 5 | Check reminder | `<brand> is waiting for you to check what Brew read`, then `Still waiting: check what Brew 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 | [src: brew/confirm-remind.js:21, 70-77] |
-| 6 | Feed reminder | `Open your feed to start <brand>’s moves`, then `Still waiting: open your feed to start <brand>’s moves`, button `Open my feed` | The same people, while the Plays wait for the first feed open | [src: 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 | [src: brew/build.js:996-1003, 1012-1027] |
-| 8 | Weekly digest | Subject built from what is new, such as `<n> new moves and <n> updated moves for <brand> this week`, or `What people said about <brand> this week`. Title `This week for <brand>`. Sections `Waiting on you`, `Moves Brew rewrote`, `New this week`, `Rival ads Brew 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 | [src: brew/digest.js:10-14, 59-63, 125-151] |
-| 9 | Tagged in a jam | `<first name> tagged you in “<jam>”`, button `Open the jam` | The tagged person, right away | [src: brew/jam-mail.js:36] |
-| 10 | Asked in a jam | `<first name> asked you a question on “<jam>”`, button `Answer it` | The person asked, right away | [src: brew/jam-mail.js:37] |
-| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: brew/jam-mail.js:38] |
-| 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 | [src: brew/jam-mail.js:39-40] |
-| 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 | [src: brew/jam-mail.js:4-5, 74-77] |
-| 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 Brew` | Every jam email | [src: brew/jam-mail.js:29] |
-| 15 | Brief status | `Your brief “<jam>” is received`, `The brief “<jam>” is delivered`, `The ShopOS 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 | [src: brew/jam-mail.js:135-147] |
-| 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 | [src: brew/connectors/index.js:53, 480-493] |
-
-All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and the demo site sends none. [src: brew/auth.js:174-177; brew/jam-mail.js:7]
+| 1 | Someone joined | `<name> joined <brand>`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: 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 | [src: brew/auth.js:190-191] |
+| 3 | Admin handed over | `You're now the admin of <brand>`, button `Open <brand>` | The new admin | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: brew/jam-mail.js:38] |
+| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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. [src: brew/auth.js:175-177; brew/mail-layout.js:55, 68; brew/product-name.js:6; brew/jam-mail.js:8]
@@ -55,2 +55,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 17 | Our team tagged in a jam | `<brand>: <first name> tagged the ShopOS team on “<jam>”`, at most once an hour per jam | [src: brew/jam-mail.js:100-110] |
-| 18 | A brief sent to our team | `BR-<n> · New brief from <brand>: “<jam>”` | [src: brew/jam-mail.js:112-133] |
+| 17 | Our team tagged in a jam | `<brand>: <first name> tagged our team on “<jam>”`, at most once an hour per jam | [src: 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>”` | [src: brew/jam-mail.js:113-134] |
@@ -63,2 +63,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 20 | Weekly post: `*This week in Brew for <brand>*` with the shared lines of the digest and `Open the feed` | [src: brew/digest.js:154-166] |
-| 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.` | [src: brew/jam-brief.js:346] |
+| 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` | [src: 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.` | [src: brew/jam-brief.js:347] |
@@ -70,5 +70,5 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from Brew`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:91-97] |
-| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: brew/auth.js:262, 266, 270] |
-| 24 | Store not open yet | `This store isn’t open yet`, `It’s still behind Shopify’s password page, so Brew shows it to the store’s own team until it opens.` | [src: brew/closed-gate.js:31-32] |
-| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:6, 18-50] |
-| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:251-252] |
+| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from us`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:92-98] |
+| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: 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.` | [src: brew/closed-gate.js:31-32] |
+| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:7, 19-51] |
+| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:252-253] |
@@ -80,16 +80,16 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: brew/public/app.js:405] |
-| 28 | Outsider wall | `<brand> is for <brand>’s team`, `Just looking around? The demo stores show Brew end to end, with nothing to set up.` | [src: brew/public/app.js:286-290] |
-| 29 | Demo mode sheet | `You’re seeing <brand> in demo mode` | [src: brew/public/app.js:89] |
-| 30 | A read refused | `Brew reads a brand for its own team. Sign in with your work email at <host> to read it.` | [src: brew/server.js:761] |
-| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: brew/build.js:215] |
-| 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.` | [src: brew/build.js:226-227] |
-| 33 | Check card | `Check what we read for <brand>`, `The brand brief and the plays are written from what you confirm.` | [src: brew/public/app.js:1632-1633] |
-| 34 | Plays paused | `The moves are paused`, `<n> of <N> plays in your plan are written.` | [src: brew/public/app.js:1614] |
-| 35 | Writing paused | `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966] |
-| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:445] |
-| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. Brew will learn from it.` | [src: brew/public/app.js:1930, 2538] |
-| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: brew/public/app.js:2087] |
-| 39 | Brief to our team | `Brief <BR-n> sent to the ShopOS team: the brief, deliverables, assets, rules and when it is done.` | [src: brew/jam-brief.js:395] |
-| 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.` | [src: brew/jam-brief.js:398] |
-| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: brew/public/app.js:4600-4601] |
-| 42 | Final making | `Making the final <kind>. It takes a few minutes; we’ll notify you when it’s ready.` | [src: brew/public/app.js:4061] |
+| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: 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.` | [src: 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.` | [src: 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.` | [src: brew/server.js:762] |
+| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: 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.` | [src: 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.` | [src: brew/public/app.js:1633] |
+| 34 | Plays paused | `The plays are paused`, `<n> of <N> plays in your plan are written.` | [src: 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.` | [src: brew/public/app.js:2967] |
+| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:446] |
+| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. We’ll learn from it.` | [src: brew/public/app.js:1931, 2539] |
+| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: 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.` | [src: 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.` | [src: brew/jam-brief.js:399] |
+| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: 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.` | [src: brew/public/app.js:4062] |
@@ -97,3 +97,3 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: brew/build.js:1102] |
-| 45 | Delete my data | `This sends a deletion request to the Brew team from your own email. We delete your data within 7 days and reply when it’s done.` | [src: brew/public/live.js:348-349] |
-| 46 | Brief status refused | `Only the ShopOS team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:407; brew/brief-status.js:26] |
+| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: 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.` | [src: brew/public/live.js:348-349] |
+| 46 | Brief status refused | `Only our team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:408; brew/brief-status.js:26] |
@@ -103,2 +103,3 @@ What the list shows:
-- Customer emails, pages and toasts name the product as Brew and our team as the ShopOS team, against the writing rules. [src: brew/auth.js:125; brew/jam-mail.js:29, 143]
-- The digest and the feed reminder still say moves, while the Plays emails say plays. [src: brew/digest.js:136; brew/confirm-remind.js:84; brew/build.js:1012]
+- 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. [src: 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. [src: brew/digest.js:131, 137; brew/confirm-remind.js:84; brew/build.js:1013]
+- `test/product-name.test.js` fails a customer template that says ShopOS or moves, or names the product other than from `product-name.js`. [src: brew/test/product-name.test.js:1-16]
@@ -113,8 +114,8 @@ What the list shows:
-| `<name> joined <brand> on Brew` | `<name> joined <brand>` | v1, Changes |
-| `<who> invited you to <brand> on Brew` | `<who> invited you to <brand>` | v1, Changes |
-| `check what Brew read` | `check what we read` | v1, Changes |
-| `Open your feed to start <brand>’s moves` | `Open your feed to start <brand>’s Plays` | v1, Changes |
-| `Moves Brew rewrote`, `new moves` | `Plays we rewrote`, `new Plays` | v1, Changes |
-| `No emails from Brew` | `No emails from us` | v1, Changes |
-| `The ShopOS team needs something for` | `Our team needs something for` | v1, Changes |
-| `Brew <hello@send.joinsquareup.com>` | The wordmark slot until the name is picked | v1, Changes |
+| `<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 |
@@ -183 +184 @@ Words Buzz may use around the controls: “Pass the pollen” for Hand it off, 
-   cd /Users/param/Documents/ChatGPT/brew && node --test test/honest-copy.test.js test/jam-mail.test.js test/shopos-name.test.js
+   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
@@ -197 +198 @@ Words Buzz may use around the controls: “Pass the pollen” for Hand it off, 
-1. Do we strip the product name and our company name from every customer message now, before the name is picked? Proposal: yes, in v1, with the wordmark slot where the name would go.
+1. 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.
diff --git a/content/customer-success-and-support/every-message.cooker.md b/content/customer-success-and-support/every-message.cooker.md
index 805b3d7..89b42fa 100644
--- a/content/customer-success-and-support/every-message.cooker.md
+++ b/content/customer-success-and-support/every-message.cooker.md
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: auth.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
+  - "brew origin/main d6154b3: auth.js, product-name.js, mail-layout.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
@@ -26 +26 @@ Every email, page and in product message the product sends a brand today, with i
-Every message the product sends or shows a brand today, read from brew origin/main a559de6. Placeholders in angle brackets are filled in by the code.
+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.
@@ -32,18 +32,18 @@ Every message the product sends or shows a brand today, read from brew origin/ma
-| 1 | Someone joined | `<name> joined <brand> on Brew`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: brew/auth.js:125-126] |
-| 2 | Invite | `<who> invited you to <brand> on Brew`, button `Join <brand>`, small print `The link works for 14 days.` | The invited person | [src: brew/auth.js:189-190] |
-| 3 | Admin handed over | `You're now the admin of <brand> on Brew`, button `Open <brand>` | The new admin | [src: brew/auth.js:209] |
-| 4 | Admin given or taken | `You're now an admin of <brand> on Brew` or `You're no longer an admin of <brand> on Brew` | That person, unless they made the change | [src: brew/auth.js:211-212] |
-| 5 | Check reminder | `<brand> is waiting for you to check what Brew read`, then `Still waiting: check what Brew 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 | [src: brew/confirm-remind.js:21, 70-77] |
-| 6 | Feed reminder | `Open your feed to start <brand>’s moves`, then `Still waiting: open your feed to start <brand>’s moves`, button `Open my feed` | The same people, while the Plays wait for the first feed open | [src: 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 | [src: brew/build.js:996-1003, 1012-1027] |
-| 8 | Weekly digest | Subject built from what is new, such as `<n> new moves and <n> updated moves for <brand> this week`, or `What people said about <brand> this week`. Title `This week for <brand>`. Sections `Waiting on you`, `Moves Brew rewrote`, `New this week`, `Rival ads Brew 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 | [src: brew/digest.js:10-14, 59-63, 125-151] |
-| 9 | Tagged in a jam | `<first name> tagged you in “<jam>”`, button `Open the jam` | The tagged person, right away | [src: brew/jam-mail.js:36] |
-| 10 | Asked in a jam | `<first name> asked you a question on “<jam>”`, button `Answer it` | The person asked, right away | [src: brew/jam-mail.js:37] |
-| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: brew/jam-mail.js:38] |
-| 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 | [src: brew/jam-mail.js:39-40] |
-| 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 | [src: brew/jam-mail.js:4-5, 74-77] |
-| 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 Brew` | Every jam email | [src: brew/jam-mail.js:29] |
-| 15 | Brief status | `Your brief “<jam>” is received`, `The brief “<jam>” is delivered`, `The ShopOS 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 | [src: brew/jam-mail.js:135-147] |
-| 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 | [src: brew/connectors/index.js:53, 480-493] |
-
-All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and the demo site sends none. [src: brew/auth.js:174-177; brew/jam-mail.js:7]
+| 1 | Someone joined | `<name> joined <brand>`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: 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 | [src: brew/auth.js:190-191] |
+| 3 | Admin handed over | `You're now the admin of <brand>`, button `Open <brand>` | The new admin | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: brew/jam-mail.js:38] |
+| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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. [src: brew/auth.js:175-177; brew/mail-layout.js:55, 68; brew/product-name.js:6; brew/jam-mail.js:8]
@@ -55,2 +55,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 17 | Our team tagged in a jam | `<brand>: <first name> tagged the ShopOS team on “<jam>”`, at most once an hour per jam | [src: brew/jam-mail.js:100-110] |
-| 18 | A brief sent to our team | `BR-<n> · New brief from <brand>: “<jam>”` | [src: brew/jam-mail.js:112-133] |
+| 17 | Our team tagged in a jam | `<brand>: <first name> tagged our team on “<jam>”`, at most once an hour per jam | [src: 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>”` | [src: brew/jam-mail.js:113-134] |
@@ -63,2 +63,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 20 | Weekly post: `*This week in Brew for <brand>*` with the shared lines of the digest and `Open the feed` | [src: brew/digest.js:154-166] |
-| 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.` | [src: brew/jam-brief.js:346] |
+| 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` | [src: 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.` | [src: brew/jam-brief.js:347] |
@@ -70,5 +70,5 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from Brew`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:91-97] |
-| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: brew/auth.js:262, 266, 270] |
-| 24 | Store not open yet | `This store isn’t open yet`, `It’s still behind Shopify’s password page, so Brew shows it to the store’s own team until it opens.` | [src: brew/closed-gate.js:31-32] |
-| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:6, 18-50] |
-| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:251-252] |
+| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from us`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:92-98] |
+| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: 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.` | [src: brew/closed-gate.js:31-32] |
+| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:7, 19-51] |
+| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:252-253] |
@@ -80,16 +80,16 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: brew/public/app.js:405] |
-| 28 | Outsider wall | `<brand> is for <brand>’s team`, `Just looking around? The demo stores show Brew end to end, with nothing to set up.` | [src: brew/public/app.js:286-290] |
-| 29 | Demo mode sheet | `You’re seeing <brand> in demo mode` | [src: brew/public/app.js:89] |
-| 30 | A read refused | `Brew reads a brand for its own team. Sign in with your work email at <host> to read it.` | [src: brew/server.js:761] |
-| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: brew/build.js:215] |
-| 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.` | [src: brew/build.js:226-227] |
-| 33 | Check card | `Check what we read for <brand>`, `The brand brief and the plays are written from what you confirm.` | [src: brew/public/app.js:1632-1633] |
-| 34 | Plays paused | `The moves are paused`, `<n> of <N> plays in your plan are written.` | [src: brew/public/app.js:1614] |
-| 35 | Writing paused | `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966] |
-| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:445] |
-| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. Brew will learn from it.` | [src: brew/public/app.js:1930, 2538] |
-| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: brew/public/app.js:2087] |
-| 39 | Brief to our team | `Brief <BR-n> sent to the ShopOS team: the brief, deliverables, assets, rules and when it is done.` | [src: brew/jam-brief.js:395] |
-| 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.` | [src: brew/jam-brief.js:398] |
-| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: brew/public/app.js:4600-4601] |
-| 42 | Final making | `Making the final <kind>. It takes a few minutes; we’ll notify you when it’s ready.` | [src: brew/public/app.js:4061] |
+| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: 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.` | [src: 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.` | [src: 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.` | [src: brew/server.js:762] |
+| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: 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.` | [src: 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.` | [src: brew/public/app.js:1633] |
+| 34 | Plays paused | `The plays are paused`, `<n> of <N> plays in your plan are written.` | [src: 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.` | [src: brew/public/app.js:2967] |
+| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:446] |
+| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. We’ll learn from it.` | [src: brew/public/app.js:1931, 2539] |
+| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: 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.` | [src: 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.` | [src: brew/jam-brief.js:399] |
+| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: 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.` | [src: brew/public/app.js:4062] |
@@ -97,3 +97,3 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: brew/build.js:1102] |
-| 45 | Delete my data | `This sends a deletion request to the Brew team from your own email. We delete your data within 7 days and reply when it’s done.` | [src: brew/public/live.js:348-349] |
-| 46 | Brief status refused | `Only the ShopOS team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:407; brew/brief-status.js:26] |
+| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: 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.` | [src: brew/public/live.js:348-349] |
+| 46 | Brief status refused | `Only our team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:408; brew/brief-status.js:26] |
@@ -103,2 +103,3 @@ What the list shows:
-- Customer emails, pages and toasts name the product as Brew and our team as the ShopOS team, against the writing rules. [src: brew/auth.js:125; brew/jam-mail.js:29, 143]
-- The digest and the feed reminder still say moves, while the Plays emails say plays. [src: brew/digest.js:136; brew/confirm-remind.js:84; brew/build.js:1012]
+- 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. [src: 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. [src: brew/digest.js:131, 137; brew/confirm-remind.js:84; brew/build.js:1013]
+- `test/product-name.test.js` fails a customer template that says ShopOS or moves, or names the product other than from `product-name.js`. [src: brew/test/product-name.test.js:1-16]
@@ -113,8 +114,8 @@ What the list shows:
-| `<name> joined <brand> on Brew` | `<name> joined <brand>` | v1, Changes |
-| `<who> invited you to <brand> on Brew` | `<who> invited you to <brand>` | v1, Changes |
-| `check what Brew read` | `check what we read` | v1, Changes |
-| `Open your feed to start <brand>’s moves` | `Open your feed to start <brand>’s Plays` | v1, Changes |
-| `Moves Brew rewrote`, `new moves` | `Plays we rewrote`, `new Plays` | v1, Changes |
-| `No emails from Brew` | `No emails from us` | v1, Changes |
-| `The ShopOS team needs something for` | `Our team needs something for` | v1, Changes |
-| `Brew <hello@send.joinsquareup.com>` | The wordmark slot until the name is picked | v1, Changes |
+| `<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 |
@@ -184 +185 @@ Words Seeti may use around the controls: “Order ticket” for Hand it off, “
-   cd /Users/param/Documents/ChatGPT/brew && node --test test/honest-copy.test.js test/jam-mail.test.js test/shopos-name.test.js
+   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
@@ -198 +199 @@ Words Seeti may use around the controls: “Order ticket” for Hand it off, “
-1. Do we strip the product name and our company name from every customer message now, before the name is picked? Proposal: yes, in v1, with the wordmark slot where the name would go.
+1. 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.
diff --git a/content/customer-success-and-support/every-message.dog.md b/content/customer-success-and-support/every-message.dog.md
index 6c2ae37..93cf80d 100644
--- a/content/customer-success-and-support/every-message.dog.md
+++ b/content/customer-success-and-support/every-message.dog.md
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: auth.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
+  - "brew origin/main d6154b3: auth.js, product-name.js, mail-layout.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
@@ -26 +26 @@ Every email, page and in product message the product sends a brand today, with i
-Every message the product sends or shows a brand today, read from brew origin/main a559de6. Placeholders in angle brackets are filled in by the code.
+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.
@@ -32,18 +32,18 @@ Every message the product sends or shows a brand today, read from brew origin/ma
-| 1 | Someone joined | `<name> joined <brand> on Brew`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: brew/auth.js:125-126] |
-| 2 | Invite | `<who> invited you to <brand> on Brew`, button `Join <brand>`, small print `The link works for 14 days.` | The invited person | [src: brew/auth.js:189-190] |
-| 3 | Admin handed over | `You're now the admin of <brand> on Brew`, button `Open <brand>` | The new admin | [src: brew/auth.js:209] |
-| 4 | Admin given or taken | `You're now an admin of <brand> on Brew` or `You're no longer an admin of <brand> on Brew` | That person, unless they made the change | [src: brew/auth.js:211-212] |
-| 5 | Check reminder | `<brand> is waiting for you to check what Brew read`, then `Still waiting: check what Brew 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 | [src: brew/confirm-remind.js:21, 70-77] |
-| 6 | Feed reminder | `Open your feed to start <brand>’s moves`, then `Still waiting: open your feed to start <brand>’s moves`, button `Open my feed` | The same people, while the Plays wait for the first feed open | [src: 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 | [src: brew/build.js:996-1003, 1012-1027] |
-| 8 | Weekly digest | Subject built from what is new, such as `<n> new moves and <n> updated moves for <brand> this week`, or `What people said about <brand> this week`. Title `This week for <brand>`. Sections `Waiting on you`, `Moves Brew rewrote`, `New this week`, `Rival ads Brew 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 | [src: brew/digest.js:10-14, 59-63, 125-151] |
-| 9 | Tagged in a jam | `<first name> tagged you in “<jam>”`, button `Open the jam` | The tagged person, right away | [src: brew/jam-mail.js:36] |
-| 10 | Asked in a jam | `<first name> asked you a question on “<jam>”`, button `Answer it` | The person asked, right away | [src: brew/jam-mail.js:37] |
-| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: brew/jam-mail.js:38] |
-| 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 | [src: brew/jam-mail.js:39-40] |
-| 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 | [src: brew/jam-mail.js:4-5, 74-77] |
-| 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 Brew` | Every jam email | [src: brew/jam-mail.js:29] |
-| 15 | Brief status | `Your brief “<jam>” is received`, `The brief “<jam>” is delivered`, `The ShopOS 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 | [src: brew/jam-mail.js:135-147] |
-| 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 | [src: brew/connectors/index.js:53, 480-493] |
-
-All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and the demo site sends none. [src: brew/auth.js:174-177; brew/jam-mail.js:7]
+| 1 | Someone joined | `<name> joined <brand>`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: 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 | [src: brew/auth.js:190-191] |
+| 3 | Admin handed over | `You're now the admin of <brand>`, button `Open <brand>` | The new admin | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: brew/jam-mail.js:38] |
+| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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. [src: brew/auth.js:175-177; brew/mail-layout.js:55, 68; brew/product-name.js:6; brew/jam-mail.js:8]
@@ -55,2 +55,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 17 | Our team tagged in a jam | `<brand>: <first name> tagged the ShopOS team on “<jam>”`, at most once an hour per jam | [src: brew/jam-mail.js:100-110] |
-| 18 | A brief sent to our team | `BR-<n> · New brief from <brand>: “<jam>”` | [src: brew/jam-mail.js:112-133] |
+| 17 | Our team tagged in a jam | `<brand>: <first name> tagged our team on “<jam>”`, at most once an hour per jam | [src: 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>”` | [src: brew/jam-mail.js:113-134] |
@@ -63,2 +63,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 20 | Weekly post: `*This week in Brew for <brand>*` with the shared lines of the digest and `Open the feed` | [src: brew/digest.js:154-166] |
-| 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.` | [src: brew/jam-brief.js:346] |
+| 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` | [src: 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.` | [src: brew/jam-brief.js:347] |
@@ -70,5 +70,5 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from Brew`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:91-97] |
-| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: brew/auth.js:262, 266, 270] |
-| 24 | Store not open yet | `This store isn’t open yet`, `It’s still behind Shopify’s password page, so Brew shows it to the store’s own team until it opens.` | [src: brew/closed-gate.js:31-32] |
-| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:6, 18-50] |
-| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:251-252] |
+| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from us`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:92-98] |
+| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: 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.` | [src: brew/closed-gate.js:31-32] |
+| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:7, 19-51] |
+| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:252-253] |
@@ -80,16 +80,16 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: brew/public/app.js:405] |
-| 28 | Outsider wall | `<brand> is for <brand>’s team`, `Just looking around? The demo stores show Brew end to end, with nothing to set up.` | [src: brew/public/app.js:286-290] |
-| 29 | Demo mode sheet | `You’re seeing <brand> in demo mode` | [src: brew/public/app.js:89] |
-| 30 | A read refused | `Brew reads a brand for its own team. Sign in with your work email at <host> to read it.` | [src: brew/server.js:761] |
-| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: brew/build.js:215] |
-| 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.` | [src: brew/build.js:226-227] |
-| 33 | Check card | `Check what we read for <brand>`, `The brand brief and the plays are written from what you confirm.` | [src: brew/public/app.js:1632-1633] |
-| 34 | Plays paused | `The moves are paused`, `<n> of <N> plays in your plan are written.` | [src: brew/public/app.js:1614] |
-| 35 | Writing paused | `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966] |
-| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:445] |
-| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. Brew will learn from it.` | [src: brew/public/app.js:1930, 2538] |
-| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: brew/public/app.js:2087] |
-| 39 | Brief to our team | `Brief <BR-n> sent to the ShopOS team: the brief, deliverables, assets, rules and when it is done.` | [src: brew/jam-brief.js:395] |
-| 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.` | [src: brew/jam-brief.js:398] |
-| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: brew/public/app.js:4600-4601] |
-| 42 | Final making | `Making the final <kind>. It takes a few minutes; we’ll notify you when it’s ready.` | [src: brew/public/app.js:4061] |
+| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: 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.` | [src: 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.` | [src: 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.` | [src: brew/server.js:762] |
+| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: 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.` | [src: 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.` | [src: brew/public/app.js:1633] |
+| 34 | Plays paused | `The plays are paused`, `<n> of <N> plays in your plan are written.` | [src: 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.` | [src: brew/public/app.js:2967] |
+| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:446] |
+| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. We’ll learn from it.` | [src: brew/public/app.js:1931, 2539] |
+| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: 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.` | [src: 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.` | [src: brew/jam-brief.js:399] |
+| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: 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.` | [src: brew/public/app.js:4062] |
@@ -97,3 +97,3 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: brew/build.js:1102] |
-| 45 | Delete my data | `This sends a deletion request to the Brew team from your own email. We delete your data within 7 days and reply when it’s done.` | [src: brew/public/live.js:348-349] |
-| 46 | Brief status refused | `Only the ShopOS team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:407; brew/brief-status.js:26] |
+| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: 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.` | [src: brew/public/live.js:348-349] |
+| 46 | Brief status refused | `Only our team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:408; brew/brief-status.js:26] |
@@ -103,2 +103,3 @@ What the list shows:
-- Customer emails, pages and toasts name the product as Brew and our team as the ShopOS team, against the writing rules. [src: brew/auth.js:125; brew/jam-mail.js:29, 143]
-- The digest and the feed reminder still say moves, while the Plays emails say plays. [src: brew/digest.js:136; brew/confirm-remind.js:84; brew/build.js:1012]
+- 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. [src: 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. [src: brew/digest.js:131, 137; brew/confirm-remind.js:84; brew/build.js:1013]
+- `test/product-name.test.js` fails a customer template that says ShopOS or moves, or names the product other than from `product-name.js`. [src: brew/test/product-name.test.js:1-16]
@@ -113,8 +114,8 @@ What the list shows:
-| `<name> joined <brand> on Brew` | `<name> joined <brand>` | v1, Changes |
-| `<who> invited you to <brand> on Brew` | `<who> invited you to <brand>` | v1, Changes |
-| `check what Brew read` | `check what we read` | v1, Changes |
-| `Open your feed to start <brand>’s moves` | `Open your feed to start <brand>’s Plays` | v1, Changes |
-| `Moves Brew rewrote`, `new moves` | `Plays we rewrote`, `new Plays` | v1, Changes |
-| `No emails from Brew` | `No emails from us` | v1, Changes |
-| `The ShopOS team needs something for` | `Our team needs something for` | v1, Changes |
-| `Brew <hello@send.joinsquareup.com>` | The wordmark slot until the name is picked | v1, Changes |
+| `<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 |
@@ -182 +183 @@ Words Fetch may use around the controls: “Drop it” for Hand it off, “Chew
-   cd /Users/param/Documents/ChatGPT/brew && node --test test/honest-copy.test.js test/jam-mail.test.js test/shopos-name.test.js
+   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
@@ -196 +197 @@ Words Fetch may use around the controls: “Drop it” for Hand it off, “Chew
-1. Do we strip the product name and our company name from every customer message now, before the name is picked? Proposal: yes, in v1, with the wordmark slot where the name would go.
+1. 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.
diff --git a/content/customer-success-and-support/every-message.octopus.md b/content/customer-success-and-support/every-message.octopus.md
index 247b110..cc60047 100644
--- a/content/customer-success-and-support/every-message.octopus.md
+++ b/content/customer-success-and-support/every-message.octopus.md
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: auth.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
+  - "brew origin/main d6154b3: auth.js, product-name.js, mail-layout.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
@@ -26 +26 @@ Every email, page and in product message the product sends a brand today, with i
-Every message the product sends or shows a brand today, read from brew origin/main a559de6. Placeholders in angle brackets are filled in by the code.
+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.
@@ -32,18 +32,18 @@ Every message the product sends or shows a brand today, read from brew origin/ma
-| 1 | Someone joined | `<name> joined <brand> on Brew`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: brew/auth.js:125-126] |
-| 2 | Invite | `<who> invited you to <brand> on Brew`, button `Join <brand>`, small print `The link works for 14 days.` | The invited person | [src: brew/auth.js:189-190] |
-| 3 | Admin handed over | `You're now the admin of <brand> on Brew`, button `Open <brand>` | The new admin | [src: brew/auth.js:209] |
-| 4 | Admin given or taken | `You're now an admin of <brand> on Brew` or `You're no longer an admin of <brand> on Brew` | That person, unless they made the change | [src: brew/auth.js:211-212] |
-| 5 | Check reminder | `<brand> is waiting for you to check what Brew read`, then `Still waiting: check what Brew 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 | [src: brew/confirm-remind.js:21, 70-77] |
-| 6 | Feed reminder | `Open your feed to start <brand>’s moves`, then `Still waiting: open your feed to start <brand>’s moves`, button `Open my feed` | The same people, while the Plays wait for the first feed open | [src: 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 | [src: brew/build.js:996-1003, 1012-1027] |
-| 8 | Weekly digest | Subject built from what is new, such as `<n> new moves and <n> updated moves for <brand> this week`, or `What people said about <brand> this week`. Title `This week for <brand>`. Sections `Waiting on you`, `Moves Brew rewrote`, `New this week`, `Rival ads Brew 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 | [src: brew/digest.js:10-14, 59-63, 125-151] |
-| 9 | Tagged in a jam | `<first name> tagged you in “<jam>”`, button `Open the jam` | The tagged person, right away | [src: brew/jam-mail.js:36] |
-| 10 | Asked in a jam | `<first name> asked you a question on “<jam>”`, button `Answer it` | The person asked, right away | [src: brew/jam-mail.js:37] |
-| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: brew/jam-mail.js:38] |
-| 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 | [src: brew/jam-mail.js:39-40] |
-| 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 | [src: brew/jam-mail.js:4-5, 74-77] |
-| 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 Brew` | Every jam email | [src: brew/jam-mail.js:29] |
-| 15 | Brief status | `Your brief “<jam>” is received`, `The brief “<jam>” is delivered`, `The ShopOS 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 | [src: brew/jam-mail.js:135-147] |
-| 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 | [src: brew/connectors/index.js:53, 480-493] |
-
-All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and the demo site sends none. [src: brew/auth.js:174-177; brew/jam-mail.js:7]
+| 1 | Someone joined | `<name> joined <brand>`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: 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 | [src: brew/auth.js:190-191] |
+| 3 | Admin handed over | `You're now the admin of <brand>`, button `Open <brand>` | The new admin | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: brew/jam-mail.js:38] |
+| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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. [src: brew/auth.js:175-177; brew/mail-layout.js:55, 68; brew/product-name.js:6; brew/jam-mail.js:8]
@@ -55,2 +55,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 17 | Our team tagged in a jam | `<brand>: <first name> tagged the ShopOS team on “<jam>”`, at most once an hour per jam | [src: brew/jam-mail.js:100-110] |
-| 18 | A brief sent to our team | `BR-<n> · New brief from <brand>: “<jam>”` | [src: brew/jam-mail.js:112-133] |
+| 17 | Our team tagged in a jam | `<brand>: <first name> tagged our team on “<jam>”`, at most once an hour per jam | [src: 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>”` | [src: brew/jam-mail.js:113-134] |
@@ -63,2 +63,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 20 | Weekly post: `*This week in Brew for <brand>*` with the shared lines of the digest and `Open the feed` | [src: brew/digest.js:154-166] |
-| 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.` | [src: brew/jam-brief.js:346] |
+| 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` | [src: 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.` | [src: brew/jam-brief.js:347] |
@@ -70,5 +70,5 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from Brew`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:91-97] |
-| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: brew/auth.js:262, 266, 270] |
-| 24 | Store not open yet | `This store isn’t open yet`, `It’s still behind Shopify’s password page, so Brew shows it to the store’s own team until it opens.` | [src: brew/closed-gate.js:31-32] |
-| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:6, 18-50] |
-| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:251-252] |
+| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from us`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:92-98] |
+| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: 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.` | [src: brew/closed-gate.js:31-32] |
+| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:7, 19-51] |
+| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:252-253] |
@@ -80,16 +80,16 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: brew/public/app.js:405] |
-| 28 | Outsider wall | `<brand> is for <brand>’s team`, `Just looking around? The demo stores show Brew end to end, with nothing to set up.` | [src: brew/public/app.js:286-290] |
-| 29 | Demo mode sheet | `You’re seeing <brand> in demo mode` | [src: brew/public/app.js:89] |
-| 30 | A read refused | `Brew reads a brand for its own team. Sign in with your work email at <host> to read it.` | [src: brew/server.js:761] |
-| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: brew/build.js:215] |
-| 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.` | [src: brew/build.js:226-227] |
-| 33 | Check card | `Check what we read for <brand>`, `The brand brief and the plays are written from what you confirm.` | [src: brew/public/app.js:1632-1633] |
-| 34 | Plays paused | `The moves are paused`, `<n> of <N> plays in your plan are written.` | [src: brew/public/app.js:1614] |
-| 35 | Writing paused | `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966] |
-| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:445] |
-| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. Brew will learn from it.` | [src: brew/public/app.js:1930, 2538] |
-| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: brew/public/app.js:2087] |
-| 39 | Brief to our team | `Brief <BR-n> sent to the ShopOS team: the brief, deliverables, assets, rules and when it is done.` | [src: brew/jam-brief.js:395] |
-| 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.` | [src: brew/jam-brief.js:398] |
-| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: brew/public/app.js:4600-4601] |
-| 42 | Final making | `Making the final <kind>. It takes a few minutes; we’ll notify you when it’s ready.` | [src: brew/public/app.js:4061] |
+| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: 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.` | [src: 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.` | [src: 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.` | [src: brew/server.js:762] |
+| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: 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.` | [src: 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.` | [src: brew/public/app.js:1633] |
+| 34 | Plays paused | `The plays are paused`, `<n> of <N> plays in your plan are written.` | [src: 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.` | [src: brew/public/app.js:2967] |
+| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:446] |
+| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. We’ll learn from it.` | [src: brew/public/app.js:1931, 2539] |
+| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: 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.` | [src: 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.` | [src: brew/jam-brief.js:399] |
+| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: 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.` | [src: brew/public/app.js:4062] |
@@ -97,3 +97,3 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: brew/build.js:1102] |
-| 45 | Delete my data | `This sends a deletion request to the Brew team from your own email. We delete your data within 7 days and reply when it’s done.` | [src: brew/public/live.js:348-349] |
-| 46 | Brief status refused | `Only the ShopOS team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:407; brew/brief-status.js:26] |
+| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: 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.` | [src: brew/public/live.js:348-349] |
+| 46 | Brief status refused | `Only our team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:408; brew/brief-status.js:26] |
@@ -103,2 +103,3 @@ What the list shows:
-- Customer emails, pages and toasts name the product as Brew and our team as the ShopOS team, against the writing rules. [src: brew/auth.js:125; brew/jam-mail.js:29, 143]
-- The digest and the feed reminder still say moves, while the Plays emails say plays. [src: brew/digest.js:136; brew/confirm-remind.js:84; brew/build.js:1012]
+- 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. [src: 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. [src: brew/digest.js:131, 137; brew/confirm-remind.js:84; brew/build.js:1013]
+- `test/product-name.test.js` fails a customer template that says ShopOS or moves, or names the product other than from `product-name.js`. [src: brew/test/product-name.test.js:1-16]
@@ -113,8 +114,8 @@ What the list shows:
-| `<name> joined <brand> on Brew` | `<name> joined <brand>` | v1, Changes |
-| `<who> invited you to <brand> on Brew` | `<who> invited you to <brand>` | v1, Changes |
-| `check what Brew read` | `check what we read` | v1, Changes |
-| `Open your feed to start <brand>’s moves` | `Open your feed to start <brand>’s Plays` | v1, Changes |
-| `Moves Brew rewrote`, `new moves` | `Plays we rewrote`, `new Plays` | v1, Changes |
-| `No emails from Brew` | `No emails from us` | v1, Changes |
-| `The ShopOS team needs something for` | `Our team needs something for` | v1, Changes |
-| `Brew <hello@send.joinsquareup.com>` | The wordmark slot until the name is picked | v1, Changes |
+| `<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 |
@@ -182 +183 @@ Words Ocho may use around the controls: “Free an arm” for Hand it off, “In
-   cd /Users/param/Documents/ChatGPT/brew && node --test test/honest-copy.test.js test/jam-mail.test.js test/shopos-name.test.js
+   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
@@ -196 +197 @@ Words Ocho may use around the controls: “Free an arm” for Hand it off, “In
-1. Do we strip the product name and our company name from every customer message now, before the name is picked? Proposal: yes, in v1, with the wordmark slot where the name would go.
+1. 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.
diff --git a/content/customer-success-and-support/every-message.raccoon.md b/content/customer-success-and-support/every-message.raccoon.md
index 07f5ced..6778c61 100644
--- a/content/customer-success-and-support/every-message.raccoon.md
+++ b/content/customer-success-and-support/every-message.raccoon.md
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: auth.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
+  - "brew origin/main d6154b3: auth.js, product-name.js, mail-layout.js, confirm-remind.js, build.js, digest.js, jam-mail.js, jam-brief.js, brief-status.js, connectors/index.js, alerts.js, closed-gate.js, legal-pages.js, shopify-app.js, server.js, public/app.js, public/live.js, line numbers in Today"
@@ -26 +26 @@ Every email, page and in product message the product sends a brand today, with i
-Every message the product sends or shows a brand today, read from brew origin/main a559de6. Placeholders in angle brackets are filled in by the code.
+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.
@@ -32,18 +32,18 @@ Every message the product sends or shows a brand today, read from brew origin/ma
-| 1 | Someone joined | `<name> joined <brand> on Brew`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: brew/auth.js:125-126] |
-| 2 | Invite | `<who> invited you to <brand> on Brew`, button `Join <brand>`, small print `The link works for 14 days.` | The invited person | [src: brew/auth.js:189-190] |
-| 3 | Admin handed over | `You're now the admin of <brand> on Brew`, button `Open <brand>` | The new admin | [src: brew/auth.js:209] |
-| 4 | Admin given or taken | `You're now an admin of <brand> on Brew` or `You're no longer an admin of <brand> on Brew` | That person, unless they made the change | [src: brew/auth.js:211-212] |
-| 5 | Check reminder | `<brand> is waiting for you to check what Brew read`, then `Still waiting: check what Brew 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 | [src: brew/confirm-remind.js:21, 70-77] |
-| 6 | Feed reminder | `Open your feed to start <brand>’s moves`, then `Still waiting: open your feed to start <brand>’s moves`, button `Open my feed` | The same people, while the Plays wait for the first feed open | [src: 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 | [src: brew/build.js:996-1003, 1012-1027] |
-| 8 | Weekly digest | Subject built from what is new, such as `<n> new moves and <n> updated moves for <brand> this week`, or `What people said about <brand> this week`. Title `This week for <brand>`. Sections `Waiting on you`, `Moves Brew rewrote`, `New this week`, `Rival ads Brew 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 | [src: brew/digest.js:10-14, 59-63, 125-151] |
-| 9 | Tagged in a jam | `<first name> tagged you in “<jam>”`, button `Open the jam` | The tagged person, right away | [src: brew/jam-mail.js:36] |
-| 10 | Asked in a jam | `<first name> asked you a question on “<jam>”`, button `Answer it` | The person asked, right away | [src: brew/jam-mail.js:37] |
-| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: brew/jam-mail.js:38] |
-| 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 | [src: brew/jam-mail.js:39-40] |
-| 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 | [src: brew/jam-mail.js:4-5, 74-77] |
-| 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 Brew` | Every jam email | [src: brew/jam-mail.js:29] |
-| 15 | Brief status | `Your brief “<jam>” is received`, `The brief “<jam>” is delivered`, `The ShopOS 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 | [src: brew/jam-mail.js:135-147] |
-| 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 | [src: brew/connectors/index.js:53, 480-493] |
-
-All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and the demo site sends none. [src: brew/auth.js:174-177; brew/jam-mail.js:7]
+| 1 | Someone joined | `<name> joined <brand>`, button `Review their views` | The admins, when a teammate signs in with the company domain | [src: 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 | [src: brew/auth.js:190-191] |
+| 3 | Admin handed over | `You're now the admin of <brand>`, button `Open <brand>` | The new admin | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: brew/jam-mail.js:38] |
+| 11 | Given a jam | `<first name> gave you “<jam>”`, button `Open the jam` | The assignee, right away | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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 | [src: 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. [src: brew/auth.js:175-177; brew/mail-layout.js:55, 68; brew/product-name.js:6; brew/jam-mail.js:8]
@@ -55,2 +55,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 17 | Our team tagged in a jam | `<brand>: <first name> tagged the ShopOS team on “<jam>”`, at most once an hour per jam | [src: brew/jam-mail.js:100-110] |
-| 18 | A brief sent to our team | `BR-<n> · New brief from <brand>: “<jam>”` | [src: brew/jam-mail.js:112-133] |
+| 17 | Our team tagged in a jam | `<brand>: <first name> tagged our team on “<jam>”`, at most once an hour per jam | [src: 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>”` | [src: brew/jam-mail.js:113-134] |
@@ -63,2 +63,2 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 20 | Weekly post: `*This week in Brew for <brand>*` with the shared lines of the digest and `Open the feed` | [src: brew/digest.js:154-166] |
-| 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.` | [src: brew/jam-brief.js:346] |
+| 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` | [src: 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.` | [src: brew/jam-brief.js:347] |
@@ -70,5 +70,5 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from Brew`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:91-97] |
-| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: brew/auth.js:262, 266, 270] |
-| 24 | Store not open yet | `This store isn’t open yet`, `It’s still behind Shopify’s password page, so Brew shows it to the store’s own team until it opens.` | [src: brew/closed-gate.js:31-32] |
-| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:6, 18-50] |
-| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:251-252] |
+| 22 | Email settings | `Muted`, `Only when you’re tagged`, `No emails from us`, `Jam emails are on`, `Nothing changed`, `This link has expired` | [src: brew/jam-mail.js:92-98] |
+| 23 | Invites | `This invite has expired`, `This invite is no longer valid`, `Access is by invite now` | [src: 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.` | [src: brew/closed-gate.js:31-32] |
+| 25 | Privacy, Terms, not found | `Preview policy · 19 October 2026`, `We couldn’t find that page` | [src: brew/legal-pages.js:7, 19-51] |
+| 26 | Shopify links | `Link expired`, `Link used` | [src: brew/shopify-app.js:252-253] |
@@ -80,16 +80,16 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: brew/public/app.js:405] |
-| 28 | Outsider wall | `<brand> is for <brand>’s team`, `Just looking around? The demo stores show Brew end to end, with nothing to set up.` | [src: brew/public/app.js:286-290] |
-| 29 | Demo mode sheet | `You’re seeing <brand> in demo mode` | [src: brew/public/app.js:89] |
-| 30 | A read refused | `Brew reads a brand for its own team. Sign in with your work email at <host> to read it.` | [src: brew/server.js:761] |
-| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: brew/build.js:215] |
-| 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.` | [src: brew/build.js:226-227] |
-| 33 | Check card | `Check what we read for <brand>`, `The brand brief and the plays are written from what you confirm.` | [src: brew/public/app.js:1632-1633] |
-| 34 | Plays paused | `The moves are paused`, `<n> of <N> plays in your plan are written.` | [src: brew/public/app.js:1614] |
-| 35 | Writing paused | `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966] |
-| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:445] |
-| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. Brew will learn from it.` | [src: brew/public/app.js:1930, 2538] |
-| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: brew/public/app.js:2087] |
-| 39 | Brief to our team | `Brief <BR-n> sent to the ShopOS team: the brief, deliverables, assets, rules and when it is done.` | [src: brew/jam-brief.js:395] |
-| 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.` | [src: brew/jam-brief.js:398] |
-| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: brew/public/app.js:4600-4601] |
-| 42 | Final making | `Making the final <kind>. It takes a few minutes; we’ll notify you when it’s ready.` | [src: brew/public/app.js:4061] |
+| 27 | Landing | `Your store’s marketing team, in one feed.` | [src: 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.` | [src: 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.` | [src: 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.` | [src: brew/server.js:762] |
+| 31 | Not a website | `That doesn’t look like a website. Try yourbrand.com` | [src: 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.` | [src: 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.` | [src: brew/public/app.js:1633] |
+| 34 | Plays paused | `The plays are paused`, `<n> of <N> plays in your plan are written.` | [src: 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.` | [src: brew/public/app.js:2967] |
+| 36 | Sign in failed | `Google sign-in did not finish. Try again.` | [src: brew/public/app.js:446] |
+| 37 | Feed teaching | `Hidden. <agent> will learn from it.`, `Thanks. We’ll learn from it.` | [src: brew/public/app.js:1931, 2539] |
+| 38 | Link copied | `Link copied. It opens this exactly as it is now.` | [src: 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.` | [src: 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.` | [src: brew/jam-brief.js:399] |
+| 41 | Gmail draft | `Gmail draft opened.` and `That didn’t go through. Try again.` | [src: 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.` | [src: brew/public/app.js:4062] |
@@ -97,3 +97,3 @@ All of these come from `Brew <hello@send.joinsquareup.com>` through Resend, and
-| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: brew/build.js:1102] |
-| 45 | Delete my data | `This sends a deletion request to the Brew team from your own email. We delete your data within 7 days and reply when it’s done.` | [src: brew/public/live.js:348-349] |
-| 46 | Brief status refused | `Only the ShopOS team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:407; brew/brief-status.js:26] |
+| 44 | Daily budget | `Today’s budget is used up. Try again tomorrow.` | [src: 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.` | [src: brew/public/live.js:348-349] |
+| 46 | Brief status refused | `Only our team moves a brief on.`, `Say what you need from them.` | [src: brew/jam-brief.js:408; brew/brief-status.js:26] |
@@ -103,2 +103,3 @@ What the list shows:
-- Customer emails, pages and toasts name the product as Brew and our team as the ShopOS team, against the writing rules. [src: brew/auth.js:125; brew/jam-mail.js:29, 143]
-- The digest and the feed reminder still say moves, while the Plays emails say plays. [src: brew/digest.js:136; brew/confirm-remind.js:84; brew/build.js:1012]
+- 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. [src: 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. [src: brew/digest.js:131, 137; brew/confirm-remind.js:84; brew/build.js:1013]
+- `test/product-name.test.js` fails a customer template that says ShopOS or moves, or names the product other than from `product-name.js`. [src: brew/test/product-name.test.js:1-16]
@@ -113,8 +114,8 @@ What the list shows:
-| `<name> joined <brand> on Brew` | `<name> joined <brand>` | v1, Changes |
-| `<who> invited you to <brand> on Brew` | `<who> invited you to <brand>` | v1, Changes |
-| `check what Brew read` | `check what we read` | v1, Changes |
-| `Open your feed to start <brand>’s moves` | `Open your feed to start <brand>’s Plays` | v1, Changes |
-| `Moves Brew rewrote`, `new moves` | `Plays we rewrote`, `new Plays` | v1, Changes |
-| `No emails from Brew` | `No emails from us` | v1, Changes |
-| `The ShopOS team needs something for` | `Our team needs something for` | v1, Changes |
-| `Brew <hello@send.joinsquareup.com>` | The wordmark slot until the name is picked | v1, Changes |
+| `<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 |
@@ -184 +185 @@ Words Scoop may use around the controls: “Pass the note” for Hand it off, 
-   cd /Users/param/Documents/ChatGPT/brew && node --test test/honest-copy.test.js test/jam-mail.test.js test/shopos-name.test.js
+   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
@@ -198 +199 @@ Words Scoop may use around the controls: “Pass the note” for Hand it off, 
-1. Do we strip the product name and our company name from every customer message now, before the name is picked? Proposal: yes, in v1, with the wordmark slot where the name would go.
+1. 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.
diff --git a/content/customer-success-and-support/help-centre.bee.md b/content/customer-success-and-support/help-centre.bee.md
index e8e1cdb..ab102ce 100644
--- a/content/customer-success-and-support/help-centre.bee.md
+++ b/content/customer-success-and-support/help-centre.bee.md
@@ -16 +16 @@ sources:
-  - "brew/jam-brief.js:360-363"
+  - "brew/jam-brief.js:361-363"
@@ -54 +54 @@ The facts each article below rests on:
-| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:360-363] |
+| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:361-363] |
@@ -129 +129 @@ Lines Buzz never says, in any of these:
-2. Is the 7 day deletion promise ours to keep on production? Proposal: yes, once the production deletion job exists; until then an engineer does it by hand within 7 days.
+2. Is the 7 day deletion promise ours to keep on production? Proposal: yes. The deletion tool, `db/delete-brand.mjs`, is on origin/main and reaches production with the next promote; until then an engineer does it by hand within 7 days.
diff --git a/content/customer-success-and-support/help-centre.cooker.md b/content/customer-success-and-support/help-centre.cooker.md
index 7fa5074..caf05a2 100644
--- a/content/customer-success-and-support/help-centre.cooker.md
+++ b/content/customer-success-and-support/help-centre.cooker.md
@@ -16 +16 @@ sources:
-  - "brew/jam-brief.js:360-363"
+  - "brew/jam-brief.js:361-363"
@@ -54 +54 @@ The facts each article below rests on:
-| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:360-363] |
+| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:361-363] |
@@ -130 +130 @@ Lines Seeti never says, in any of these:
-2. Is the 7 day deletion promise ours to keep on production? Proposal: yes, once the production deletion job exists; until then an engineer does it by hand within 7 days.
+2. Is the 7 day deletion promise ours to keep on production? Proposal: yes. The deletion tool, `db/delete-brand.mjs`, is on origin/main and reaches production with the next promote; until then an engineer does it by hand within 7 days.
diff --git a/content/customer-success-and-support/help-centre.dog.md b/content/customer-success-and-support/help-centre.dog.md
index 76f2b56..8023258 100644
--- a/content/customer-success-and-support/help-centre.dog.md
+++ b/content/customer-success-and-support/help-centre.dog.md
@@ -16 +16 @@ sources:
-  - "brew/jam-brief.js:360-363"
+  - "brew/jam-brief.js:361-363"
@@ -54 +54 @@ The facts each article below rests on:
-| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:360-363] |
+| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:361-363] |
@@ -128 +128 @@ Lines Fetch never says, in any of these:
-2. Is the 7 day deletion promise ours to keep on production? Proposal: yes, once the production deletion job exists; until then an engineer does it by hand within 7 days.
+2. Is the 7 day deletion promise ours to keep on production? Proposal: yes. The deletion tool, `db/delete-brand.mjs`, is on origin/main and reaches production with the next promote; until then an engineer does it by hand within 7 days.
diff --git a/content/customer-success-and-support/help-centre.octopus.md b/content/customer-success-and-support/help-centre.octopus.md
index 1c90ef8..8c53d8c 100644
--- a/content/customer-success-and-support/help-centre.octopus.md
+++ b/content/customer-success-and-support/help-centre.octopus.md
@@ -16 +16 @@ sources:
-  - "brew/jam-brief.js:360-363"
+  - "brew/jam-brief.js:361-363"
@@ -54 +54 @@ The facts each article below rests on:
-| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:360-363] |
+| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:361-363] |
@@ -128 +128 @@ Lines Ocho never says, in any of these:
-2. Is the 7 day deletion promise ours to keep on production? Proposal: yes, once the production deletion job exists; until then an engineer does it by hand within 7 days.
+2. Is the 7 day deletion promise ours to keep on production? Proposal: yes. The deletion tool, `db/delete-brand.mjs`, is on origin/main and reaches production with the next promote; until then an engineer does it by hand within 7 days.
diff --git a/content/customer-success-and-support/help-centre.raccoon.md b/content/customer-success-and-support/help-centre.raccoon.md
index 572e5ee..a1a3a90 100644
--- a/content/customer-success-and-support/help-centre.raccoon.md
+++ b/content/customer-success-and-support/help-centre.raccoon.md
@@ -16 +16 @@ sources:
-  - "brew/jam-brief.js:360-363"
+  - "brew/jam-brief.js:361-363"
@@ -54 +54 @@ The facts each article below rests on:
-| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:360-363] |
+| A brief to the brand's own team is copied, never sent. A brief to our team is emailed and tracked. | [src: brew/jam-brief.js:361-363] |
@@ -130 +130 @@ Lines Scoop never says, in any of these:
-2. Is the 7 day deletion promise ours to keep on production? Proposal: yes, once the production deletion job exists; until then an engineer does it by hand within 7 days.
+2. Is the 7 day deletion promise ours to keep on production? Proposal: yes. The deletion tool, `db/delete-brand.mjs`, is on origin/main and reaches production with the next promote; until then an engineer does it by hand within 7 days.
diff --git a/content/customer-success-and-support/known-issues.md b/content/customer-success-and-support/known-issues.md
index 1dc1352..0591a7e 100644
--- a/content/customer-success-and-support/known-issues.md
+++ b/content/customer-success-and-support/known-issues.md
@@ -18 +18,2 @@ sources:
-  - "brew origin/main a559de6: jam-brief.js:405-415, jam-mail.js:129, public/live.js:349, legal-pages.js:33, auth.js:125, 177, 189, digest.js:136, confirm-remind.js:84, build.js:1012, finals commit 0fc7cfc, tools/reset-brand.sh:1-13"
+  - "brew origin/main d6154b3: public/brief-staff.js:1-16, jam-brief.js:406-416, public/live.js:349, legal-pages.js:34, auth.js:177, build.js:1013, 1031, mail-layout.js:55, 68, digest.js:137, 167, confirm-remind.js:84, product-name.js:1-6, db/delete-brand.mjs:1-10, docs/production.md:95-106, finals commit 0fc7cfc"
+  - "brew commits 2eed121, e3e0fb4, ca2bc7b; deploy state 26 Oct: staging deploying, production unchanged"
@@ -35,5 +36,5 @@ Newest first. The date is the date of the source. An issue fixed since its note
-| There is no button for our team to move a sent brief through Received, In progress, Needs info and Delivered. The brief email tells us to move it on from the jam, but only the API route exists. | An engineer calls the status route for the brief. The partner sees Sent until then. | [src: brew/jam-brief.js:405-415; brew/jam-mail.js:129] |
-| The in app deletion sheet promises deletion within 7 days. The privacy page gives no time. | Answer every deletion request within 7 days, which keeps both true. | [src: brew/public/live.js:349; brew/legal-pages.js:33] |
-| Production has no deletion tool. `tools/reset-brand.sh` works on the staging bucket only, and production runs on Postgres. | A deletion on production is done by hand by an engineer. See data requests. | [src: brew/tools/reset-brand.sh:1-13; memory brew-prod-backend] |
-| Emails name the product, Brew, in subjects, footers and the sender, and name our team as the ShopOS team. Both break the customer writing rules. | None yet. Listed in every message as a v1 change. | [src: brew/auth.js:125, 177, 189; brew/jam-mail.js:29, 143] |
-| Some emails still say moves where every other screen says Plays, such as the weekly digest and the open your feed reminder. | None yet. | [src: brew/digest.js:136; brew/confirm-remind.js:84; brew/build.js:1012] |
+| On production there is no button for our team to move a sent brief on. The buttons are on brew origin/main and going to staging now; production gets them with the next promote. | Until then an engineer calls the status route on production. The partner sees Sent until then. | [src: brew/public/brief-staff.js:1-16; brew/jam-brief.js:406-416; deploy state 26 Oct] |
+| The in app deletion sheet promises deletion within 7 days. The privacy page gives no time. | Answer every deletion request within 7 days, which keeps both true. | [src: brew/public/live.js:349; brew/legal-pages.js:34] |
+| Production has no deletion job yet. `db/delete-brand.mjs` is on origin/main, but production needs the next promote for its `data_deletions` table and a `brew-prod-ops` job made once. | On staging run the `brew-ops` job. On production wait for the promote, or an engineer deletes by hand. See data requests. | [src: brew/db/delete-brand.mjs:1-10; brew/docs/production.md:95-106] |
+| Emails still name the product in the sender, the header and the footer. On origin/main the name comes from one constant, still Brew, and the ShopOS team is gone from every email. Production still sends the old wording until the next promote. | None yet. The rename is one line once the name is picked. | [src: brew/auth.js:177; brew/build.js:1031; brew/mail-layout.js:55, 68; brew/product-name.js:1-6] |
+| Emails on production still say moves, such as the weekly digest and the open your feed reminder. Fixed on origin/main: they say plays. | None needed after the next promote. | [src: brew/digest.js:137; brew/confirm-remind.js:84; brew/build.js:1013] |
@@ -48 +49 @@ Newest first. The date is the date of the source. An issue fixed since its note
-| The product is still named Brew in the interface. No display filter swaps it yet. | None yet. | [src: memory brew-jam-rca-2026-10-26] |
+| The product is still named Brew in the interface. Since 26 Oct on origin/main the name is one constant, and about 880 customer strings say we or our team instead. A few still name it, such as the mail sender and the Slack app. | None yet. | [src: memory brew-jam-rca-2026-10-26; brew/product-name.js:1-6; brew/auth.js:177; brew/docs/slack-app-manifest.json:4, 10; brew commit ca2bc7b] |
@@ -73 +74 @@ Newest first. The date is the date of the source. An issue fixed since its note
-| Our team is named three ways across Plays. | 20 Oct | [src: memory brew-qa-audit-2026-10-20] |
+| Our team is named three ways across Plays. Customer strings were changed to our team on origin/main on 26 Oct; not re-checked across Plays since. | 20 Oct | [src: memory brew-qa-audit-2026-10-20; brew commit ca2bc7b] |
@@ -83,3 +84,3 @@ Newest first. The date is the date of the source. An issue fixed since its note
-| v1 | Changes | Brief status buttons for our team inside the jam, so no brief waits on an engineer. |
-| v1 | Changes | Customer emails drop the product name and our company name, and say Plays. |
-| v1 | New | A production deletion job, so the 7 day promise does not rest on a manual edit. |
+| v1 | Stays | Brief status buttons for our team inside the jam, built on origin/main; on production after the next promote. |
+| v1 | Changes | Customer emails drop the product name from the sender, header and footer once the name is picked. Our company name and moves are gone on origin/main. |
+| v1 | Changes | The production deletion job, built on origin/main, made on production after the next promote, so the 7 day promise does not rest on a manual edit. |
@@ -110 +111 @@ Newest first. The date is the date of the source. An issue fixed since its note
-2. The interface still says Brew. Do we filter the name out of the product before the five partners use it daily? Proposal: yes, a display filter in v1 that says we, as the jam RCA listed.
+2. The interface still says Brew in a few places, now from one constant. Do we set that constant to the picked name, or to none, before the five partners use it daily? Proposal: keep Brew until the name is picked, then change the one line.
diff --git a/content/customer-success-and-support/notices-to-customers.bee.md b/content/customer-success-and-support/notices-to-customers.bee.md
index 65c95fe..f5eba78 100644
--- a/content/customer-success-and-support/notices-to-customers.bee.md
+++ b/content/customer-success-and-support/notices-to-customers.bee.md
@@ -11 +11 @@ sources:
-  - "brew/public/app.js:1608-1614, 2965-2966"
+  - "brew/public/app.js:1609-1615, 2966-2967"
@@ -14 +14 @@ sources:
-  - "brew/docs/production.md:68-69"
+  - "brew/docs/production.md:114-115"
@@ -35,2 +35,2 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Writing paused | A one time toast when AI writing is paused: `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2965-2966] |
-| Plays paused | The feed's paused card: `The moves are paused` with how many Plays are written | [src: brew/public/app.js:1608-1614] |
+| Writing paused | A one time toast when AI writing is paused: `Our writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966-2967] |
+| Plays paused | The feed's paused card: `The plays are paused` with how many Plays are written | [src: brew/public/app.js:1609-1615] |
@@ -40 +40 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:68-69] |
+| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:114-115] |
diff --git a/content/customer-success-and-support/notices-to-customers.cooker.md b/content/customer-success-and-support/notices-to-customers.cooker.md
index c8ec3cf..d18a19e 100644
--- a/content/customer-success-and-support/notices-to-customers.cooker.md
+++ b/content/customer-success-and-support/notices-to-customers.cooker.md
@@ -11 +11 @@ sources:
-  - "brew/public/app.js:1608-1614, 2965-2966"
+  - "brew/public/app.js:1609-1615, 2966-2967"
@@ -14 +14 @@ sources:
-  - "brew/docs/production.md:68-69"
+  - "brew/docs/production.md:114-115"
@@ -35,2 +35,2 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Writing paused | A one time toast when AI writing is paused: `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2965-2966] |
-| Plays paused | The feed's paused card: `The moves are paused` with how many Plays are written | [src: brew/public/app.js:1608-1614] |
+| Writing paused | A one time toast when AI writing is paused: `Our writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966-2967] |
+| Plays paused | The feed's paused card: `The plays are paused` with how many Plays are written | [src: brew/public/app.js:1609-1615] |
@@ -40 +40 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:68-69] |
+| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:114-115] |
diff --git a/content/customer-success-and-support/notices-to-customers.dog.md b/content/customer-success-and-support/notices-to-customers.dog.md
index 52a2390..02676c9 100644
--- a/content/customer-success-and-support/notices-to-customers.dog.md
+++ b/content/customer-success-and-support/notices-to-customers.dog.md
@@ -11 +11 @@ sources:
-  - "brew/public/app.js:1608-1614, 2965-2966"
+  - "brew/public/app.js:1609-1615, 2966-2967"
@@ -14 +14 @@ sources:
-  - "brew/docs/production.md:68-69"
+  - "brew/docs/production.md:114-115"
@@ -35,2 +35,2 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Writing paused | A one time toast when AI writing is paused: `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2965-2966] |
-| Plays paused | The feed's paused card: `The moves are paused` with how many Plays are written | [src: brew/public/app.js:1608-1614] |
+| Writing paused | A one time toast when AI writing is paused: `Our writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966-2967] |
+| Plays paused | The feed's paused card: `The plays are paused` with how many Plays are written | [src: brew/public/app.js:1609-1615] |
@@ -40 +40 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:68-69] |
+| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:114-115] |
diff --git a/content/customer-success-and-support/notices-to-customers.octopus.md b/content/customer-success-and-support/notices-to-customers.octopus.md
index 7feed62..c5fe29a 100644
--- a/content/customer-success-and-support/notices-to-customers.octopus.md
+++ b/content/customer-success-and-support/notices-to-customers.octopus.md
@@ -11 +11 @@ sources:
-  - "brew/public/app.js:1608-1614, 2965-2966"
+  - "brew/public/app.js:1609-1615, 2966-2967"
@@ -14 +14 @@ sources:
-  - "brew/docs/production.md:68-69"
+  - "brew/docs/production.md:114-115"
@@ -35,2 +35,2 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Writing paused | A one time toast when AI writing is paused: `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2965-2966] |
-| Plays paused | The feed's paused card: `The moves are paused` with how many Plays are written | [src: brew/public/app.js:1608-1614] |
+| Writing paused | A one time toast when AI writing is paused: `Our writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966-2967] |
+| Plays paused | The feed's paused card: `The plays are paused` with how many Plays are written | [src: brew/public/app.js:1609-1615] |
@@ -40 +40 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:68-69] |
+| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:114-115] |
diff --git a/content/customer-success-and-support/notices-to-customers.raccoon.md b/content/customer-success-and-support/notices-to-customers.raccoon.md
index 08518d8..6b399ac 100644
--- a/content/customer-success-and-support/notices-to-customers.raccoon.md
+++ b/content/customer-success-and-support/notices-to-customers.raccoon.md
@@ -11 +11 @@ sources:
-  - "brew/public/app.js:1608-1614, 2965-2966"
+  - "brew/public/app.js:1609-1615, 2966-2967"
@@ -14 +14 @@ sources:
-  - "brew/docs/production.md:68-69"
+  - "brew/docs/production.md:114-115"
@@ -35,2 +35,2 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Writing paused | A one time toast when AI writing is paused: `Brew’s writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2965-2966] |
-| Plays paused | The feed's paused card: `The moves are paused` with how many Plays are written | [src: brew/public/app.js:1608-1614] |
+| Writing paused | A one time toast when AI writing is paused: `Our writing is paused for a while. Everything here still opens; new replies, drafts and rewrites wait until it’s back.` | [src: brew/public/app.js:2966-2967] |
+| Plays paused | The feed's paused card: `The plays are paused` with how many Plays are written | [src: brew/public/app.js:1609-1615] |
@@ -40 +40 @@ No notice system exists. Nothing tells a brand about an incident, maintenance or
-| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:68-69] |
+| Monitoring to us only | Uptime and alert policies email param@ and hello@ | [src: brew/docs/production.md:114-115] |
diff --git a/content/customer-success-and-support/support-process.md b/content/customer-success-and-support/support-process.md
index a04279b..3ab3395 100644
--- a/content/customer-success-and-support/support-process.md
+++ b/content/customer-success-and-support/support-process.md
@@ -14 +14 @@ sources:
-  - "brew/docs/production.md:68-69"
+  - "brew/docs/production.md:114-115"
@@ -35 +35 @@ How a partner reaches us, who answers and how fast; the code gives us one inbox
-| Platform monitoring | Cloud Monitoring emails param@ and hello@ on uptime and alert policies | [src: brew/docs/production.md:68-69] |
+| Platform monitoring | Cloud Monitoring emails param@ and hello@ on uptime and alert policies | [src: brew/docs/production.md:114-115] |
diff --git a/content/data-and-analytics/dashboards.md b/content/data-and-analytics/dashboards.md
index 4cca512..a6dfdc1 100644
--- a/content/data-and-analytics/dashboards.md
+++ b/content/data-and-analytics/dashboards.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew/docs/production.md:66-69"
+  - "brew/docs/production.md:112-115"
@@ -29 +29 @@ No dashboard of company numbers exists; today we read health from Cloud Monitori
-| Cloud Monitoring | Uptime of `/`, `/healthz/ready`, `/api/config`; 11 alert conditions | param@ and hello@ by email | [src: brew/docs/production.md:66-69] |
+| Cloud Monitoring | Uptime of `/`, `/healthz/ready`, `/api/config`; 11 alert conditions | param@ and hello@ by email | [src: brew/docs/production.md:112-115] |
diff --git a/content/data-and-analytics/the-68-data-sources.md b/content/data-and-analytics/the-68-data-sources.md
index 6c45ccd..a3c9632 100644
--- a/content/data-and-analytics/the-68-data-sources.md
+++ b/content/data-and-analytics/the-68-data-sources.md
@@ -92 +92 @@ Every source the product reads: 68, with cadence and status.
-[src: brew origin/main a559de6]
+[src: brew origin/main 45b6a06]
diff --git a/content/engineering/changelog.md b/content/engineering/changelog.md
index 1cf741c..bf2a6d0 100644
--- a/content/engineering/changelog.md
+++ b/content/engineering/changelog.md
@@ -10 +10 @@ sources:
-  - git log brew origin/main a559de6
+  - git log brew origin/main 45b6a06
@@ -17 +17 @@ sources:
-What shipped to Brew, day by day: 866 changes since 12 October.
+What shipped to Brew, day by day: 877 changes since 12 October.
@@ -22,0 +23,11 @@ What shipped to Brew, day by day: 866 changes since 12 October.
+- promote.sh: an image named by hand is labelled with its own commit and must be one staging ran `45b6a06`
+- Merge remote-tracking branch 'origin/main' into open-fixes `d6154b3`
+- Every deliverable under "+": all 228 open as a jam on any card `b9362bc`
+- Merge remote-tracking branch 'origin/clean-names' into open-fixes `f78e495`
+- Merge remote-tracking branch 'origin/staff-brief-status' into open-fixes `aff50c3`
+- Merge remote-tracking branch 'origin/db-reset-delete' into open-fixes `c43ed11`
+- Customer-facing copy: no ShopOS, plays not plays, the product name in one constant `ca2bc7b`
+- Timeouts hold the event loop until they fire or the work settles `4799f78`
+- Staff status buttons for briefs sent to our team `2eed121`
+- Postgres brand reset and data-request deletion as brew-ops jobs `e3e0fb4`
+- CI: run the deploy test gate on every push and pull request `fccf3cb`
diff --git a/content/engineering/codebase-map.md b/content/engineering/codebase-map.md
index d84a058..c1c9633 100644
--- a/content/engineering/codebase-map.md
+++ b/content/engineering/codebase-map.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10,0 +11 @@ sources:
+  - "brew origin/main d6154b3: git ls-tree -l for the rows the 26 Oct merge touched: db/, public/, test/, tools/, .github/, product-name.js, public/brief-staff.js, db/brand-wipe.js, db/wipe-cli.js, db/reset-brand.mjs, db/delete-brand.mjs"
@@ -28 +29,2 @@ How to read the tables:
-- The suite has 209 test files and about 1,314 `test()` calls, run free with `AI_MOCK=1`. [src: brew/test/] Counted from source, not from a run.
+- The suite has 215 test files and about 1,355 `test()` calls on d6154b3, run free with `AI_MOCK=1`, and on GitHub Actions on every push and pull request. [src: brew/test/] [src: brew/.github/workflows/test.yml:1-37] Counted from source, not from a run.
+- Rows for `db/`, `public/`, `test/`, `tools/`, `.github/` and the files the 26 Oct merge added are counted on d6154b3; the rest stay as counted on b91e364. [src: git ls-tree -l origin/main]
@@ -37 +39 @@ How to read the tables:
-| `db/` | Postgres pool, migrations 001 to 009, ops jobs (status, parity, import, partner copy, clean gate, reset jams, connector ping) | 19 | 62 KB | [src: brew/db/index.js:1-4] |
+| `db/` | Postgres pool, migrations 001 to 010, ops jobs (status, parity, import, partner copy, clean gate, reset jams, reset brand, delete brand, connector ping) | 24 | 91 KB | [src: brew/db/index.js:1-4] [src: brew/db/brand-wipe.js:1-9] |
@@ -39,3 +41,4 @@ How to read the tables:
-| `public/` | The client: plain JS and CSS, no framework | 163 | 4.2 MB | [src: brew/README.md] |
-| `test/` | The suite, fixtures, paid probes | 222 | 1.8 MB | [src: brew/test/] |
-| `tools/` | Deploy, promote, audits, probes, resets, Slack app, sound build | 27 | 115 KB | [src: brew/tools/] |
+| `public/` | The client: plain JS and CSS, no framework | 164 | 4.2 MB | [src: brew/README.md] |
+| `test/` | The suite, fixtures, paid probes | 228 | 1.9 MB | [src: brew/test/] |
+| `tools/` | Deploy, promote, audits, probes, resets, Slack app, sound build | 28 | 121 KB | [src: brew/tools/] |
+| `.github/` | One workflow, `test`: the deploy test gate on every push and pull request | 1 | 1.6 KB | [src: brew/.github/workflows/test.yml:1-9] |
@@ -52 +55 @@ How to read the tables:
-| `public/app.js` | 875 KB | 5,984 | [src: brew/public/app.js] |
+| `public/app.js` | 877 KB | 5,997 | [src: brew/public/app.js] |
@@ -54 +57 @@ How to read the tables:
-| `public/jam-room.js` | 80 KB | | [src: brew/public/jam-room.js] |
+| `public/jam-room.js` | 81 KB | | [src: brew/public/jam-room.js] |
@@ -81 +84 @@ No client file has a test of its own. [src: brew/test/]
-| `brief-status.js` | Status of a brief sent to our team: sent, received, in progress, needs info, delivered | brief-reference, brief-tune-drop, jam-team, sent-time-wa | 7.9 KB |
+| `brief-status.js` | Status of a brief sent to our team: sent, received, in progress, needs info, delivered | brief-reference, brief-staff, brief-tune-drop, jam-team, sent-time-wa | 7.9 KB |
@@ -169,0 +173 @@ No client file has a test of its own. [src: brew/test/]
+| `product-name.js` | The product's public name in one constant, `PRODUCT_NAME`, still "Brew", and the head script that hands it to the browser; a rename is one line | product-name, and imported by the email, page and Slack modules | 0.8 KB |
@@ -289,0 +294,4 @@ No client file has a test of its own. [src: brew/test/]
+| `db/brand-wipe.js` | What a brand reset or a data request deletion removes, the partner and protected guards, the dry run, the sweep, the audit row | brand-wipe-pg | 23.3 KB |
+| `db/wipe-cli.js` | Shared start of the two brand tools: arguments, database, bucket, stop marker | brand-wipe-pg | 2.3 KB |
+| `db/reset-brand.mjs` | One brand back to a fresh first run, connections kept | brand-wipe-pg | 1.1 KB |
+| `db/delete-brand.mjs` | A data request: all of a brand's data, one `data_deletions` row kept | brand-wipe-pg | 1.2 KB |
diff --git a/content/engineering/data-model.md b/content/engineering/data-model.md
index 89104a3..134e501 100644
--- a/content/engineering/data-model.md
+++ b/content/engineering/data-model.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main: db/migrations/001_objects.sql to 009_make_claims.sql"
+  - "brew origin/main d6154b3: db/migrations/001_objects.sql to 010_data_deletions.sql, db/brand-wipe.js:187-195"
@@ -48,0 +49 @@ Brew keeps every structured file it used to write to the bucket as a row in one
+| `data_deletions` | One row per data request deletion by `db/delete-brand.mjs`: slug, request, operator, start and end, planned and removed counts by folder, the grants the brand revokes itself, what backups still hold, rows that came back. Never contents or member names | The only thing kept of a deleted brand; `done_at` empty means it did not finish. On staging; production gets it with the next promote | [src: brew/db/migrations/010_data_deletions.sql:1-18] [src: brew/db/brand-wipe.js:187-195] |
@@ -50 +51 @@ Brew keeps every structured file it used to write to the bucket as a row in one
-The approved plan also named real tables for access, connections and secrets, Plays, jams, finals and spend, and the brand feed. Only `access_log` and `sessions` exist; the rest are still `objects` rows. [src: memory brew-prod-backend] [src: brew/db/migrations/]
+The approved plan also named real tables for access, connections and secrets, Plays, jams, finals and spend, and the brand feed. Only `access_log`, `sessions` and the deletion audit `data_deletions` exist; the rest are still `objects` rows. [src: memory brew-prod-backend] [src: brew/db/migrations/]
diff --git a/content/engineering/decision-records.md b/content/engineering/decision-records.md
index 0a78ef7..fd598fd 100644
--- a/content/engineering/decision-records.md
+++ b/content/engineering/decision-records.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -88 +88 @@ One record per big technical call, each with its context, the decision, what fol
-- **Consequences:** more than one instance became possible; per-domain tables are still to come; the bucket-era tools (`reset-jams.sh`, `reset-brand.sh`) no longer work on Postgres sites.
+- **Consequences:** more than one instance became possible; per-domain tables are still to come; the bucket-era tools (`reset-jams.sh`, `reset-brand.sh`) no longer work on Postgres sites; since 26 Oct `db/reset-brand.mjs` and `db/delete-brand.mjs` replace the brand ones, which now exit 3 unless `STORAGE=gcs` [src: brew/tools/reset-brand.sh:11-19; brew/db/reset-brand.mjs:1-11].
diff --git a/content/engineering/release-and-promote.md b/content/engineering/release-and-promote.md
index 50cfd36..a61a338 100644
--- a/content/engineering/release-and-promote.md
+++ b/content/engineering/release-and-promote.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11,0 +12 @@ sources:
+  - "brew origin/main d6154b3: .github/workflows/test.yml:1-37, db/migrations/010_data_deletions.sql, docs/production.md:95-106; commits fccf3cb, d6154b3; GitHub Actions run 40286754218"
@@ -22,0 +24,5 @@ Code reaches staging with `./deploy.sh` any time it is tested, and reaches produ
+### CI on every push
+
+- GitHub Actions workflow `test` runs the same suite `deploy.sh` runs, `AI_MOCK=1 node --test test/*.test.js`, on every push and pull request, on Node 22 with Postgres 17 binaries. First green run: https://github.com/wulfor24/brew/actions/runs/40286754218. [src: brew/.github/workflows/test.yml:1-37] [src: GitHub Actions run 40286754218]
+- It deploys nothing and `deploy.sh` does not wait for it; the deploy still runs the suite itself. [src: brew/.github/workflows/test.yml:9-37] [src: brew/deploy.sh:13-16]
+
@@ -39,0 +46 @@ Deploys never wait for background work: SIGTERM stops new Plays, flushes jams an
+| 0b | `tools/promote.sh <image@sha256:…>` promotes an earlier staging image: it must be one a staging revision ran, and production is labelled with that revision's own commit | stops | [src: git 45b6a06] |
@@ -49,0 +57,2 @@ Deploys never wait for background work: SIGTERM stops new Plays, flushes jams an
+Last promote: 26 Oct, a559de6 (the 26 Oct jam fixes, jam product truth and Higgsfield safety) went to production as brew-web-00036-sam and brew-worker-00018-wxg; the backup, clean gate and load-balancer checks passed. [src: tools/promote.sh run, Param, 26 Oct] [src: git a559de6]
+
@@ -64,0 +74,2 @@ Param's kept exceptions: the Pro plan sheet without billing, Marketplaces "Comin
+Now on origin/main and not yet on production: d6154b3, the staff brief buttons, the Postgres reset and delete tools with migration 010, the customer copy cleanup and CI. Its staging deploy was running on 26 Oct. The next promote runs migration 010, which adds the `data_deletions` table; the `brew-prod-ops` job is then made once by hand. [src: git d6154b3] [src: brew/docs/production.md:95-106] [src: deploy state 26 Oct]
+
@@ -73,0 +85,2 @@ Param's kept exceptions: the Pro plan sheet without billing, Marketplaces "Comin
+| CI on every push and pull request | v1 | Stays |
+| `deploy.sh` stops when the commit's CI run is red | v1 | New, proposed |
diff --git a/content/engineering/tech-debt.md b/content/engineering/tech-debt.md
index ed1e870..5e8ba1a 100644
--- a/content/engineering/tech-debt.md
+++ b/content/engineering/tech-debt.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -12,0 +13 @@ sources:
+  - "brew origin/main d6154b3: .github/workflows/test.yml:1-37, db/reset-brand.mjs:1-13, tools/reset-brand.sh:11-19, tools/flush-live.sh:17-26, tools/reset-jams.sh:1-6; commits fccf3cb, e3e0fb4"
@@ -45 +46 @@ Risk: **High** can hurt a partner or cost money now; **Medium** slows us or brea
-| D-11 | Bucket-era tools `tools/reset-brand.sh`, `tools/reset-jams.sh`, `tools/flush-live.sh` do not work on Postgres sites | Medium | Port to `brew-ops` jobs like `db/reset-jams.mjs`, or delete | [src: memory brew-jam-reset] |
+| D-11 | `tools/reset-jams.sh` is bucket era and still runs against the bucket on a Postgres site. Partly paid 26 Oct: `db/reset-brand.mjs` and `db/delete-brand.mjs` replace the brand tools, and `tools/reset-brand.sh` and `tools/flush-live.sh` exit 3 unless `STORAGE=gcs` | Low | Give `tools/reset-jams.sh` the same guard, then delete all three | [src: memory brew-jam-reset] [src: brew/tools/reset-jams.sh:1-6] [src: brew/tools/reset-brand.sh:11-19] [src: brew/tools/flush-live.sh:17-26] [src: brew commit e3e0fb4] |
@@ -52,2 +53,2 @@ Risk: **High** can hurt a partner or cost money now; **Medium** slows us or brea
-| D-13 | Giant files: `public/app.js` 875 KB and 5,984 lines, `feed-service.js` 340 KB, `remix.js` 305 KB, `build.js` 211 KB, `brew.css` 267 KB | Medium | Split by area behind existing tests | [src: git ls-tree -l origin/main] |
-| D-14 | No CI, lint or type checks; `deploy.sh` is the only gate; the demo deploy runs no tests | High | A GitHub Action on push; tests in deploy-demo | [src: git ls-tree origin/main] [src: brew/docs/handover/engineering/08-testing-and-qa.md] |
+| D-13 | Giant files: `public/app.js` 877 KB and 5,997 lines, `feed-service.js` 340 KB, `remix.js` 305 KB, `build.js` 211 KB, `brew.css` 267 KB | Medium | Split by area behind existing tests | [src: git ls-tree -l origin/main] |
+| D-14 | No lint or type checks; the demo deploy runs no tests. Partly paid 26 Oct: GitHub Actions runs the suite on every push and pull request | Medium | Lint and `node --check` in the Action; tests in deploy-demo | [src: brew/.github/workflows/test.yml:1-37] [src: brew/docs/handover/engineering/08-testing-and-qa.md] [src: brew commit fccf3cb] |
@@ -85 +86,2 @@ Risk: **High** can hurt a partner or cost money now; **Medium** slows us or brea
-| D-1, D-3, D-14, D-23 paid before more partners onboard | v1 | New |
+| D-1, D-3, D-23 paid before more partners onboard | v1 | New |
+| D-14 finished: lint and checks in CI | v1 | Changes |
@@ -87 +89 @@ Risk: **High** can hurt a partner or cost money now; **Medium** slows us or brea
-| D-16 to D-20 | v1 | Goes |
+| D-11, D-16 to D-20 | v1 | Goes |
@@ -99 +101 @@ Risk: **High** can hurt a partner or cost money now; **Medium** slows us or brea
-| Open debt by risk | Count of High, Medium, Low items | This page: 4 High, 16 Medium, 10 Low today |
+| Open debt by risk | Count of High, Medium, Low items | This page: 3 High, 16 Medium, 11 Low today |
@@ -104 +106 @@ Risk: **High** can hurt a partner or cost money now; **Medium** slows us or brea
-1. **Which High item first?** Proposal: D-14 (CI) this week, it is a day's work and guards everything else; then D-1.
+1. **Which High item first, now that CI runs?** Proposal: D-1 this week, then D-3.
diff --git a/content/engineering/testing-and-qa.md b/content/engineering/testing-and-qa.md
index af1e46b..a2fac4e 100644
--- a/content/engineering/testing-and-qa.md
+++ b/content/engineering/testing-and-qa.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10,3 @@ sources:
-  - "brew origin/main: test/ (209 test files, 4 paid probes), deploy.sh:13-16, README.md 'Test', CLAUDE.md"
+  - "brew origin/main d6154b3: test/ (215 test files, 4 paid probes), deploy.sh:13-16, README.md 'Test', CLAUDE.md"
+  - "brew origin/main d6154b3: .github/workflows/test.yml:1-37, listen-watch.js:81, reader/util.js:179, test/brief-staff.test.js, test/brand-wipe-pg.test.js, test/product-name.test.js"
+  - "brew commits fccf3cb, 4799f78; GitHub Actions run https://github.com/wulfor24/brew/actions/runs/40286754218"
@@ -27 +29 @@ Brew is guarded by one free test suite that every deploy runs, a set of honesty
-- 209 test files and about 1,314 `test()` calls on origin/main (counted from source). The jam audit closed on 25 Oct with 1,173 tests passing. [src: brew/test/] [src: memory brew-v1-creative-stage]
+- 215 test files and about 1,355 `test()` calls on origin/main d6154b3 (counted from source). The jam audit closed on 25 Oct with 1,173 tests passing. [src: brew/test/] [src: memory brew-v1-creative-stage]
@@ -30 +32,8 @@ Brew is guarded by one free test suite that every deploy runs, a set of honesty
-- Postgres tests (`*-pg`, `qv1-*`, `qv2-*`, `phase3-pg`) run against a local Postgres through `test/pg-helper.js`. [src: brew/test/pg-helper.js]
+- Postgres tests (`*-pg`, `qv1-*`, `qv2-*`, `phase3-pg`) run against a local Postgres through `test/pg-helper.js`, which starts a throwaway Postgres 17 cluster per file. [src: brew/test/pg-helper.js] [src: brew/.github/workflows/test.yml:22-23]
+
+### CI
+
+- GitHub Actions workflow `test` runs the same gate as `deploy.sh` on every push and every pull request: Ubuntu 24.04, Node 22 as the Dockerfile, `npm ci`, Postgres 17 binaries, then `node --test test/*.test.js` with `AI_MOCK=1`. A newer push cancels the older run; a run stops after 30 minutes. [src: brew/.github/workflows/test.yml:1-37]
+- It deploys nothing. [src: brew/.github/workflows/test.yml:9-37]
+- First green run: https://github.com/wulfor24/brew/actions/runs/40286754218. [src: GitHub Actions run 40286754218]
+- CI found that `timed()` in `listen-watch.js` and `withTimeout()` in `reader/util.js` let a job or a test file exit mid run when a source never settled; the timer is now held and cleared when the work settles. [src: brew commit 4799f78] [src: brew/listen-watch.js:81] [src: brew/reader/util.js:179]
@@ -44,0 +54,3 @@ Brew is guarded by one free test suite that every deploy runs, a set of honesty
+| `product-name.test.js` | Customer templates never say ShopOS or moves, name the product only from `product-name.js`, and every served page has a language and a description | [src: brew/test/product-name.test.js:51-84] |
+| `brief-staff.test.js` | Only our team moves a brief on; the client's steps match the server's; the controls never show to a brand member, guest or the demo | [src: brew/test/brief-staff.test.js:31-94] |
+| `brand-wipe-pg.test.js` | Reset and delete: dry run removes nothing, partners refused, connections unchanged, audit row written, bucket scripts refuse on Postgres | [src: brew/test/brand-wipe-pg.test.js:46-142] |
@@ -73 +85 @@ Brew is guarded by one free test suite that every deploy runs, a set of honesty
-Route gating has no single route table to test against; ranking (`score`, `rank`, `feed`) has no fixtures from several brands; no client file has a test; there is no CI, lint or type check. [src: brew/docs/handover/engineering/08-testing-and-qa.md] [src: git ls-tree origin/main]
+Route gating has no single route table to test against; ranking (`score`, `rank`, `feed`) has no fixtures from several brands; few client files have a test, `brief-staff.test.js` among them; there is no lint or type check, and CI runs only the suite. [src: brew/docs/handover/engineering/08-testing-and-qa.md] [src: brew/.github/workflows/test.yml:35-37]
@@ -80 +92,2 @@ Route gating has no single route table to test against; ranking (`score`, `rank`
-| A GitHub Action running `npm ci`, `node --check` and the suite on every push | v1 | New |
+| A GitHub Action running `npm ci` and the suite on every push and pull request, live since 26 Oct | v1 | Stays |
+| `node --check` and a lint step in the same Action | v1 | New, proposed |
@@ -99 +112,2 @@ Route gating has no single route table to test against; ranking (`score`, `rank`
-| Suite time | Wall time of the suite | deploy log (Proposed to record) |
+| Suite time | Wall time of the suite | deploy log, and each GitHub Actions run |
+| CI pass rate | Share of Actions runs on main that pass | GitHub Actions, workflow `test`, Proposed |
@@ -105 +119 @@ Route gating has no single route table to test against; ranking (`score`, `rank`
-1. **Do we add CI now?** Proposal: yes, a GitHub Action on push to main that runs the suite; it does not deploy.
+1. **Should a red CI run block a deploy?** Proposal: yes, `deploy.sh` checks the commit's Actions status first and stops on red.
diff --git a/content/engineering/the-jam-and-studio.md b/content/engineering/the-jam-and-studio.md
index 1537cac..7acfc04 100644
--- a/content/engineering/the-jam-and-studio.md
+++ b/content/engineering/the-jam-and-studio.md
@@ -60,0 +61 @@ A jam is the one place a card becomes finished work: v1's jam engine (`remix.js`
+- "+" on the jam page offers every deliverable in the catalogue (228), searchable by name and channel. 80 open as an action Brew already has, named as the deliverable; the other 148 are planned here: written, checked and handed over as a brief with their own rules, maker and hand-off files, then made and shipped by a person. A new deliverable is a row in `docs/deliverables-catalog.txt`, built by `tools/deliverables.mjs`. [src: brew/deliverables.js] [src: brew/data/deliverables.json] [src: git b9362bc]
diff --git a/content/integrations-and-ecosystem/data-and-making-partners.md b/content/integrations-and-ecosystem/data-and-making-partners.md
index 7354780..f7348bd 100644
--- a/content/integrations-and-ecosystem/data-and-making-partners.md
+++ b/content/integrations-and-ecosystem/data-and-making-partners.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main: treg.js:1-17, 45; apify.js:1-4; jam2/higgsfield.js:1-5, 49; jam2/engines.js:7, 60-61; finals.js; sloosh-mcp.js; budget.js:13; server.js:592-593; tools/prod-env.sh:9, 12; deploy.sh:37, 41; docs/production.md:69, 79-80; SCOPE.md:15, 18; DUMMY.md row 8"
+  - "brew origin/main: treg.js:1-17, 45; apify.js:1-4; jam2/higgsfield.js:1-5, 49; jam2/engines.js:7, 60-61; finals.js; sloosh-mcp.js; budget.js:13; server.js:592-593; tools/prod-env.sh:9, 12; deploy.sh:37, 41; docs/production.md:115, 125-126; SCOPE.md:15, 18; DUMMY.md row 8"
@@ -57 +57 @@ No source names a contact person at treg, Apify, Sloosh or Higgsfield. Account e
-The app's daily alerts cover the treg balance and Sloosh; Sloosh alerts are off on production with `SLOOSH_CHECK=0` [src: brew/docs/production.md:69] [src: brew/server.js:592-593]. Source health for every treg and Apify call is at `/api/admin/sources` [src: memory brew-apify-watch].
+The app's daily alerts cover the treg balance and Sloosh; Sloosh alerts are off on production with `SLOOSH_CHECK=0` [src: brew/docs/production.md:115] [src: brew/server.js:592-593]. Source health for every treg and Apify call is at `/api/admin/sources` [src: memory brew-apify-watch].
diff --git a/content/integrations-and-ecosystem/google.md b/content/integrations-and-ecosystem/google.md
index 9798476..5d7e06d 100644
--- a/content/integrations-and-ecosystem/google.md
+++ b/content/integrations-and-ecosystem/google.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main: connectors/google.js:1-35, 95-110; docs/connectors-approvals.md:10, 19-24; tools/prod-env.sh:9; deploy.sh:41; docs/production.md:73-75"
+  - "brew origin/main: connectors/google.js:1-35, 95-110; docs/connectors-approvals.md:10, 19-24; tools/prod-env.sh:9; deploy.sh:41; docs/production.md:119-121"
@@ -32 +32 @@ Google is the one platform where Brew's approval is done: the OAuth app for proj
-| Callbacks | `/auth/google/callback` and `/connect/<id>/callback` on both sites | [src: brew/docs/production.md:73-75] |
+| Callbacks | `/auth/google/callback` and `/connect/<id>/callback` on both sites | [src: brew/docs/production.md:119-121] |
diff --git a/content/integrations-and-ecosystem/shopify-app.md b/content/integrations-and-ecosystem/shopify-app.md
index dcc95c6..5267994 100644
--- a/content/integrations-and-ecosystem/shopify-app.md
+++ b/content/integrations-and-ecosystem/shopify-app.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main: docs/shopify-app-review.md:1-149; docs/shopify.app.toml:1-39; shopify-app.js:1-30; connectors/shopify.js:1-16, 84; docs/connectors-approvals.md:13, 30-35; docs/production.md:79-81; tools/prod-env.sh:9; legal-pages.js:25"
+  - "brew origin/main d6154b3: docs/shopify-app-review.md:1-149; docs/shopify.app.toml:1-39; shopify-app.js:1-31; connectors/shopify.js:1-16, 84; docs/connectors-approvals.md:13, 30-35; docs/production.md:125-127; tools/prod-env.sh:9; legal-pages.js:26"
@@ -36 +36 @@ Brew has one public Shopify app, "Brew", submitted for App Store review on 21 Oc
-| Admin API | GraphQL only, version `2026-04` | [src: brew/connectors/shopify.js:10] [src: brew/shopify-app.js:26] |
+| Admin API | GraphQL only, version `2026-04` | [src: brew/connectors/shopify.js:10] [src: brew/shopify-app.js:27] |
@@ -48,0 +49,2 @@ The toml sets the webhook API version to `2026-10` while the code reads the Admi
+| Listing copy in the repo | Since 26 Oct the draft names the app `Brew: Daily Marketing Plays` and says plays and we throughout. Whether the live listing in the Partner Dashboard was changed to match is not recorded | [src: brew/docs/shopify-app-review.md:107-115] [src: brew commit ca2bc7b] |
+| App name in `docs/shopify.app.toml` | Still `Brew`, left as is | [src: brew/docs/shopify.app.toml:8] |
@@ -73 +75 @@ The toml sets the webhook API version to `2026-10` while the code reads the Admi
-The app's URLs point at staging, brew.joinsquareup.com; moving it to production is listed as not done and waits for the review [src: brew/docs/shopify.app.toml:9, 19-22] [src: brew/docs/production.md:79-81].
+The app's URLs point at staging, brew.joinsquareup.com; moving it to production is listed as not done and waits for the review [src: brew/docs/shopify.app.toml:9, 19-22] [src: brew/docs/production.md:125-127].
@@ -87 +89,2 @@ Arata's Shopify connection, through its own app, is never disconnected [src: mem
-| Listing copy says Plays and follows the chosen name | v1 | Changes |
+| Listing copy says plays, done in the repo draft | v1 | Stays |
+| Listing and app name follow the chosen name | v1 | Changes |
@@ -126 +129 @@ shopify app deploy
-2. **The listing name is `Brew: Daily Marketing Moves`.** The product now says Plays and the name is open. Proposal: keep the listing as submitted until approval, then rename to the picked name with Plays in the subtitle.
+2. **The repo draft now says `Brew: Daily Marketing Plays`; the listing was submitted as `Brew: Daily Marketing Moves`.** Proposal: change the live listing to the Plays wording only after approval, so the review is not reset, then rename to the picked name.
diff --git a/content/integrations-and-ecosystem/slack.md b/content/integrations-and-ecosystem/slack.md
index 8b3a517..24db17d 100644
--- a/content/integrations-and-ecosystem/slack.md
+++ b/content/integrations-and-ecosystem/slack.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main: connectors/slack.js:1-63; slack-routes.js:1-50, 80, 89; docs/slack-app-manifest.json; docs/connectors-approvals.md:15, 36; docs/production.md:21, 73-77; tools/prod-env.sh:9; SCOPE.md:14; test/slack-relay.test.js"
+  - "brew origin/main d6154b3: connectors/slack.js:1-64; slack-routes.js:1-51, 81, 90; docs/slack-app-manifest.json; docs/connectors-approvals.md:15, 36; docs/production.md:21, 119-123; tools/prod-env.sh:9; SCOPE.md:14; test/slack-relay.test.js"
@@ -28 +28 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-| Keys | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `SLACK_SIGNING_SECRET` in Secret Manager; the connector switches on when the first two are set | [src: brew/connectors/slack.js:5-6, 10] [src: brew/docs/connectors-approvals.md:15] |
+| Keys | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `SLACK_SIGNING_SECRET` in Secret Manager; the connector switches on when the first two are set | [src: brew/connectors/slack.js:5-6, 11] [src: brew/docs/connectors-approvals.md:15] |
@@ -31 +31 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-| Bot scopes | `chat:write`, `chat:write.public`, `incoming-webhook`, `channels:join`, `channels:read`, `groups:read`, `channels:history`, `groups:history`, `users:read`, `users:read.email`, `team:read` | [src: brew/connectors/slack.js:11] [src: brew/docs/slack-app-manifest.json] |
+| Bot scopes | `chat:write`, `chat:write.public`, `incoming-webhook`, `channels:join`, `channels:read`, `groups:read`, `channels:history`, `groups:history`, `users:read`, `users:read.email`, `team:read` | [src: brew/connectors/slack.js:12] [src: brew/docs/slack-app-manifest.json] |
@@ -34 +34,2 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-| Manifest copy | Says "marketing moves" and "Brew by SquareUp" | [src: brew/docs/slack-app-manifest.json] |
+| Manifest copy | Since 26 Oct says "marketing plays" and "We read a brand's own store"; the app name and the bot are still Brew, left as is until the name is picked. Not yet pushed to Slack in any source we read | [src: brew/docs/slack-app-manifest.json:4-10] [src: brew commit ca2bc7b] |
+| Messages | Slack refusals and replies say we and us, and the name in `Invite @Brew` comes from `PRODUCT_NAME` | [src: brew/connectors/slack.js:7, 32-34] [src: brew/slack-routes.js:49-51] |
@@ -38 +39 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-1. The brand signs in with Continue with Slack and picks a channel on Slack's own screen; Brew joins that channel itself, because Slack only sends a bot replies from channels it is in [src: brew/connectors/slack.js:1-3, 49-51].
+1. The brand signs in with Continue with Slack and picks a channel on Slack's own screen; Brew joins that channel itself, because Slack only sends a bot replies from channels it is in [src: brew/connectors/slack.js:1-3, 50-52].
@@ -41,3 +42,3 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-4. Each thread message is sorted by one cheap call: chatter folds into the brief; a change or any paid image or video becomes a proposal with Make it and Not now, run once on a yes; @Brew applies a text change at once [src: memory brew-real-connectors] [src: brew/slack-routes.js:36-38].
-5. Approve moves the jam to Ready; the button must come from the workspace the brand connected, on the jam's own post [src: brew/slack-routes.js:44-50].
-6. Slack's read is the workspace and channel only: no cards, no ledger rows [src: brew/connectors/slack.js:3-4, 57-61].
+4. Each thread message is sorted by one cheap call: chatter folds into the brief; a change or any paid image or video becomes a proposal with Make it and Not now, run once on a yes; @Brew applies a text change at once [src: memory brew-real-connectors] [src: brew/slack-routes.js:37-39].
+5. Approve moves the jam to Ready; the button must come from the workspace the brand connected, on the jam's own post [src: brew/slack-routes.js:45-51].
+6. Slack's read is the workspace and channel only: no cards, no ledger rows [src: brew/connectors/slack.js:3-4, 58-62].
@@ -48,3 +49,3 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-- Every request is checked against the signing secret, at most 5 minutes old, and answered within Slack's 3 seconds [src: brew/connectors/slack.js:22-28] [src: brew/slack-routes.js:1-5].
-- Production handles workspaces connected to its brands and relays any other to staging byte for byte, with the signature headers, so staging checks it the same way; staging never relays; a forged request is never relayed [src: brew/slack-routes.js:18-25, 80, 89] [src: brew/test/slack-relay.test.js] [src: brew/tools/prod-env.sh:9].
-- Retried events are skipped for an hour by event id [src: brew/slack-routes.js:28-29].
+- Every request is checked against the signing secret, at most 5 minutes old, and answered within Slack's 3 seconds [src: brew/connectors/slack.js:23-29] [src: brew/slack-routes.js:1-6].
+- Production handles workspaces connected to its brands and relays any other to staging byte for byte, with the signature headers, so staging checks it the same way; staging never relays; a forged request is never relayed [src: brew/slack-routes.js:19-26, 81, 90] [src: brew/test/slack-relay.test.js] [src: brew/tools/prod-env.sh:9].
+- Retried events are skipped for an hour by event id [src: brew/slack-routes.js:29-30].
@@ -68 +69,2 @@ Slack is the one connector that writes: the Brew Slack app posts the cards and j
-| Manifest and messages say Plays and follow the chosen name | v1 | Changes |
+| Manifest and messages say plays, done on origin/main | v1 | Stays |
+| App name and bot follow the chosen name | v1 | Changes |
@@ -87 +89 @@ node tools/slack-app.mjs update
-4. When Slack refuses, show its plain words from `why()`, such as invite @Brew to the channel [src: brew/connectors/slack.js:30-33].
+4. When Slack refuses, show its plain words from `why()`, such as invite @Brew to the channel [src: brew/connectors/slack.js:31-34].
@@ -102 +104 @@ node tools/slack-app.mjs update
-3. **The manifest says moves and "Brew by SquareUp".** Proposal: rewrite the description with Plays and no product name once the name is picked, in one manifest update.
+3. **The manifest now says plays, and still names the app Brew.** Proposal: push the new description with `tools/slack-app.mjs update` now, and rename the app in one more update once the name is picked.
diff --git a/content/marketing-and-growth/website.bee.md b/content/marketing-and-growth/website.bee.md
index 137cdfe..3600a50 100644
--- a/content/marketing-and-growth/website.bee.md
+++ b/content/marketing-and-growth/website.bee.md
@@ -9 +9 @@ direction: bee
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/app.js:276-289, 391, 402-409; public/index.html:1-2; public/demo-picker.html:127, 186; legal-pages.js:5-8; auth.js:176; handoff.js:7, 135; server.js:937-947"
+  - "brew origin/main d6154b3: public/app.js:276-290, 392, 403-411; public/index.html:1; public/demo-picker.html:127, 186; legal-pages.js:9, 17; auth.js:177; handoff.js:8, 136; server.js:938-948; product-name.js:1-6; test/product-name.test.js:84; commit ca2bc7b"
@@ -29,10 +29,10 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:404-405] |
-| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:406] |
-| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:407] |
-| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public | [src: brew/public/app.js:391] |
-| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:409] |
-| Three sample cards with figures sit beside the box with no Sample label | [src: brew/public/app.js:402] |
-| The page title is the product's internal name and there is no description | [src: brew/public/index.html:1-2] |
-| The internal name shows on the landing, the legal pages and every email, against the rule that the product never names itself | [src: brew/public/app.js:406] [src: brew/legal-pages.js:8] [src: brew/auth.js:176] |
-| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-289] |
-| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; it still uses the old word for Plays | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
+| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:405-406] |
+| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:407] |
+| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:408] |
+| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public; this line names the product, read from `PRODUCT_NAME` | [src: brew/public/app.js:392] |
+| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:410] |
+| Three sample cards with figures sit beside the box, each labelled Sample since 26 Oct | [src: brew/public/app.js:403, 411] [src: brew commit ca2bc7b] |
+| The page title is still the product's internal name; since 26 Oct the page says its language and has a description: Your store's marketing team in one feed, plays from your own data, first drafts and one brief to hand over | [src: brew/public/index.html:1] [src: brew/test/product-name.test.js:84] |
+| The internal name still shows in a few places, the landing's own store line, the legal pages' title and nav, and every email's sender, all read from one constant so the rename is one line | [src: brew/public/app.js:392] [src: brew/legal-pages.js:9, 17] [src: brew/auth.js:177] [src: brew/product-name.js:1-6] |
+| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-290] |
+| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; since 26 Oct it says plays and we | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
@@ -40,2 +40,2 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:7] [src: brew/handoff.js:135] |
-| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:937-947] |
+| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:8] [src: brew/handoff.js:136] |
+| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:938-948] |
@@ -98 +98 @@ Buzz's line about these pages: I leave a tiny buzz at the bottom. That is all.
-| Landing sample cards | Labelled Sample, or replaced by a real partner finding with permission | v1 | Changes |
+| Landing sample cards | Labelled Sample, done on origin/main; replaced by a real partner finding with permission later | v1 | Stays |
@@ -125 +125 @@ Buzz's line about these pages: I leave a tiny buzz at the bottom. That is all.
-3. **Do the landing's sample cards stay?** Proposal: label them Sample this week.
+3. **Do the landing's Sample cards stay, now that they are labelled?** Proposal: keep them until a partner lets us show a real finding.
diff --git a/content/marketing-and-growth/website.cooker.md b/content/marketing-and-growth/website.cooker.md
index aab9494..cf94f4f 100644
--- a/content/marketing-and-growth/website.cooker.md
+++ b/content/marketing-and-growth/website.cooker.md
@@ -9 +9 @@ direction: cooker
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/app.js:276-289, 391, 402-409; public/index.html:1-2; public/demo-picker.html:127, 186; legal-pages.js:5-8; auth.js:176; handoff.js:7, 135; server.js:937-947"
+  - "brew origin/main d6154b3: public/app.js:276-290, 392, 403-411; public/index.html:1; public/demo-picker.html:127, 186; legal-pages.js:9, 17; auth.js:177; handoff.js:8, 136; server.js:938-948; product-name.js:1-6; test/product-name.test.js:84; commit ca2bc7b"
@@ -29,10 +29,10 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:404-405] |
-| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:406] |
-| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:407] |
-| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public | [src: brew/public/app.js:391] |
-| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:409] |
-| Three sample cards with figures sit beside the box with no Sample label | [src: brew/public/app.js:402] |
-| The page title is the product's internal name and there is no description | [src: brew/public/index.html:1-2] |
-| The internal name shows on the landing, the legal pages and every email, against the rule that the product never names itself | [src: brew/public/app.js:406] [src: brew/legal-pages.js:8] [src: brew/auth.js:176] |
-| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-289] |
-| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; it still uses the old word for Plays | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
+| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:405-406] |
+| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:407] |
+| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:408] |
+| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public; this line names the product, read from `PRODUCT_NAME` | [src: brew/public/app.js:392] |
+| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:410] |
+| Three sample cards with figures sit beside the box, each labelled Sample since 26 Oct | [src: brew/public/app.js:403, 411] [src: brew commit ca2bc7b] |
+| The page title is still the product's internal name; since 26 Oct the page says its language and has a description: Your store's marketing team in one feed, plays from your own data, first drafts and one brief to hand over | [src: brew/public/index.html:1] [src: brew/test/product-name.test.js:84] |
+| The internal name still shows in a few places, the landing's own store line, the legal pages' title and nav, and every email's sender, all read from one constant so the rename is one line | [src: brew/public/app.js:392] [src: brew/legal-pages.js:9, 17] [src: brew/auth.js:177] [src: brew/product-name.js:1-6] |
+| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-290] |
+| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; since 26 Oct it says plays and we | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
@@ -40,2 +40,2 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:7] [src: brew/handoff.js:135] |
-| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:937-947] |
+| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:8] [src: brew/handoff.js:136] |
+| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:938-948] |
@@ -98 +98 @@ Seeti's line about these pages: A tiny steel sign off. The ticket does the talki
-| Landing sample cards | Labelled Sample, or replaced by a real partner finding with permission | v1 | Changes |
+| Landing sample cards | Labelled Sample, done on origin/main; replaced by a real partner finding with permission later | v1 | Stays |
@@ -125 +125 @@ Seeti's line about these pages: A tiny steel sign off. The ticket does the talki
-3. **Do the landing's sample cards stay?** Proposal: label them Sample this week.
+3. **Do the landing's Sample cards stay, now that they are labelled?** Proposal: keep them until a partner lets us show a real finding.
diff --git a/content/marketing-and-growth/website.dog.md b/content/marketing-and-growth/website.dog.md
index ae21824..9c74c95 100644
--- a/content/marketing-and-growth/website.dog.md
+++ b/content/marketing-and-growth/website.dog.md
@@ -9 +9 @@ direction: dog
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/app.js:276-289, 391, 402-409; public/index.html:1-2; public/demo-picker.html:127, 186; legal-pages.js:5-8; auth.js:176; handoff.js:7, 135; server.js:937-947"
+  - "brew origin/main d6154b3: public/app.js:276-290, 392, 403-411; public/index.html:1; public/demo-picker.html:127, 186; legal-pages.js:9, 17; auth.js:177; handoff.js:8, 136; server.js:938-948; product-name.js:1-6; test/product-name.test.js:84; commit ca2bc7b"
@@ -29,10 +29,10 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:404-405] |
-| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:406] |
-| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:407] |
-| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public | [src: brew/public/app.js:391] |
-| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:409] |
-| Three sample cards with figures sit beside the box with no Sample label | [src: brew/public/app.js:402] |
-| The page title is the product's internal name and there is no description | [src: brew/public/index.html:1-2] |
-| The internal name shows on the landing, the legal pages and every email, against the rule that the product never names itself | [src: brew/public/app.js:406] [src: brew/legal-pages.js:8] [src: brew/auth.js:176] |
-| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-289] |
-| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; it still uses the old word for Plays | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
+| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:405-406] |
+| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:407] |
+| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:408] |
+| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public; this line names the product, read from `PRODUCT_NAME` | [src: brew/public/app.js:392] |
+| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:410] |
+| Three sample cards with figures sit beside the box, each labelled Sample since 26 Oct | [src: brew/public/app.js:403, 411] [src: brew commit ca2bc7b] |
+| The page title is still the product's internal name; since 26 Oct the page says its language and has a description: Your store's marketing team in one feed, plays from your own data, first drafts and one brief to hand over | [src: brew/public/index.html:1] [src: brew/test/product-name.test.js:84] |
+| The internal name still shows in a few places, the landing's own store line, the legal pages' title and nav, and every email's sender, all read from one constant so the rename is one line | [src: brew/public/app.js:392] [src: brew/legal-pages.js:9, 17] [src: brew/auth.js:177] [src: brew/product-name.js:1-6] |
+| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-290] |
+| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; since 26 Oct it says plays and we | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
@@ -40,2 +40,2 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:7] [src: brew/handoff.js:135] |
-| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:937-947] |
+| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:8] [src: brew/handoff.js:136] |
+| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:938-948] |
@@ -98 +98 @@ Fetch's line about these pages: I drop the brief at their feet and step back.
-| Landing sample cards | Labelled Sample, or replaced by a real partner finding with permission | v1 | Changes |
+| Landing sample cards | Labelled Sample, done on origin/main; replaced by a real partner finding with permission later | v1 | Stays |
@@ -125 +125 @@ Fetch's line about these pages: I drop the brief at their feet and step back.
-3. **Do the landing's sample cards stay?** Proposal: label them Sample this week.
+3. **Do the landing's Sample cards stay, now that they are labelled?** Proposal: keep them until a partner lets us show a real finding.
diff --git a/content/marketing-and-growth/website.octopus.md b/content/marketing-and-growth/website.octopus.md
index 20d8f42..67c3b74 100644
--- a/content/marketing-and-growth/website.octopus.md
+++ b/content/marketing-and-growth/website.octopus.md
@@ -9 +9 @@ direction: octopus
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/app.js:276-289, 391, 402-409; public/index.html:1-2; public/demo-picker.html:127, 186; legal-pages.js:5-8; auth.js:176; handoff.js:7, 135; server.js:937-947"
+  - "brew origin/main d6154b3: public/app.js:276-290, 392, 403-411; public/index.html:1; public/demo-picker.html:127, 186; legal-pages.js:9, 17; auth.js:177; handoff.js:8, 136; server.js:938-948; product-name.js:1-6; test/product-name.test.js:84; commit ca2bc7b"
@@ -29,10 +29,10 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:404-405] |
-| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:406] |
-| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:407] |
-| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public | [src: brew/public/app.js:391] |
-| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:409] |
-| Three sample cards with figures sit beside the box with no Sample label | [src: brew/public/app.js:402] |
-| The page title is the product's internal name and there is no description | [src: brew/public/index.html:1-2] |
-| The internal name shows on the landing, the legal pages and every email, against the rule that the product never names itself | [src: brew/public/app.js:406] [src: brew/legal-pages.js:8] [src: brew/auth.js:176] |
-| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-289] |
-| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; it still uses the old word for Plays | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
+| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:405-406] |
+| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:407] |
+| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:408] |
+| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public; this line names the product, read from `PRODUCT_NAME` | [src: brew/public/app.js:392] |
+| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:410] |
+| Three sample cards with figures sit beside the box, each labelled Sample since 26 Oct | [src: brew/public/app.js:403, 411] [src: brew commit ca2bc7b] |
+| The page title is still the product's internal name; since 26 Oct the page says its language and has a description: Your store's marketing team in one feed, plays from your own data, first drafts and one brief to hand over | [src: brew/public/index.html:1] [src: brew/test/product-name.test.js:84] |
+| The internal name still shows in a few places, the landing's own store line, the legal pages' title and nav, and every email's sender, all read from one constant so the rename is one line | [src: brew/public/app.js:392] [src: brew/legal-pages.js:9, 17] [src: brew/auth.js:177] [src: brew/product-name.js:1-6] |
+| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-290] |
+| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; since 26 Oct it says plays and we | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
@@ -40,2 +40,2 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:7] [src: brew/handoff.js:135] |
-| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:937-947] |
+| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:8] [src: brew/handoff.js:136] |
+| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:938-948] |
@@ -98 +98 @@ Ocho's line about these pages: One small arm waves at the bottom. That is it.
-| Landing sample cards | Labelled Sample, or replaced by a real partner finding with permission | v1 | Changes |
+| Landing sample cards | Labelled Sample, done on origin/main; replaced by a real partner finding with permission later | v1 | Stays |
@@ -125 +125 @@ Ocho's line about these pages: One small arm waves at the bottom. That is it.
-3. **Do the landing's sample cards stay?** Proposal: label them Sample this week.
+3. **Do the landing's Sample cards stay, now that they are labelled?** Proposal: keep them until a partner lets us show a real finding.
diff --git a/content/marketing-and-growth/website.raccoon.md b/content/marketing-and-growth/website.raccoon.md
index 6758ef0..f44560d 100644
--- a/content/marketing-and-growth/website.raccoon.md
+++ b/content/marketing-and-growth/website.raccoon.md
@@ -9 +9 @@ direction: raccoon
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/app.js:276-289, 391, 402-409; public/index.html:1-2; public/demo-picker.html:127, 186; legal-pages.js:5-8; auth.js:176; handoff.js:7, 135; server.js:937-947"
+  - "brew origin/main d6154b3: public/app.js:276-290, 392, 403-411; public/index.html:1; public/demo-picker.html:127, 186; legal-pages.js:9, 17; auth.js:177; handoff.js:8, 136; server.js:938-948; product-name.js:1-6; test/product-name.test.js:84; commit ca2bc7b"
@@ -29,10 +29,10 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:404-405] |
-| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:406] |
-| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:407] |
-| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public | [src: brew/public/app.js:391] |
-| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:409] |
-| Three sample cards with figures sit beside the box with no Sample label | [src: brew/public/app.js:402] |
-| The page title is the product's internal name and there is no description | [src: brew/public/index.html:1-2] |
-| The internal name shows on the landing, the legal pages and every email, against the rule that the product never names itself | [src: brew/public/app.js:406] [src: brew/legal-pages.js:8] [src: brew/auth.js:176] |
-| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-289] |
-| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; it still uses the old word for Plays | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
+| The live landing says Design partner preview, then Your store's marketing team, in one feed | [src: brew/public/app.js:405-406] |
+| Its lede: add your website; it reads your store, reviews, rivals and AI answers, then agents bring you ideas you shape together and hand over as one brief | [src: brew/public/app.js:407] |
+| One box with yourbrand.com as the hint, and one button, Build my feed | [src: brew/public/app.js:408] |
+| Under the box: run it on your own store, or one you work on; your team claims the workspace, others only see what is public; this line names the product, read from `PRODUCT_NAME` | [src: brew/public/app.js:392] |
+| The foot says sign in with Google, email sign in is coming, with Privacy and Terms links | [src: brew/public/app.js:410] |
+| Three sample cards with figures sit beside the box, each labelled Sample since 26 Oct | [src: brew/public/app.js:403, 411] [src: brew commit ca2bc7b] |
+| The page title is still the product's internal name; since 26 Oct the page says its language and has a description: Your store's marketing team in one feed, plays from your own data, first drafts and one brief to hand over | [src: brew/public/index.html:1] [src: brew/test/product-name.test.js:84] |
+| The internal name still shows in a few places, the landing's own store line, the legal pages' title and nav, and every email's sender, all read from one constant so the rename is one line | [src: brew/public/app.js:392] [src: brew/legal-pages.js:9, 17] [src: brew/auth.js:177] [src: brew/product-name.js:1-6] |
+| A visitor on a free mailbox meets a wall that points to the demo stores | [src: brew/public/app.js:276-290] |
+| The demo picker says know what to do next, hand it over in one brief; a read there takes about four minutes; since 26 Oct it says plays and we | [src: brew/public/demo-picker.html:127] [src: brew/public/demo-picker.html:186] |
@@ -40,2 +40,2 @@ The website is one paste box and one promise, a short scroll that shows a read,
-| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:7] [src: brew/handoff.js:135] |
-| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:937-947] |
+| The brief page is a signed read only link with no login, marked noindex | [src: brew/handoff.js:8] [src: brew/handoff.js:136] |
+| Each brand's brand book is public as a page and as files, including llms.txt | [src: brew/server.js:938-948] |
@@ -98 +98 @@ Scoop's line about these pages: I sign the note. Small. Then I vanish.
-| Landing sample cards | Labelled Sample, or replaced by a real partner finding with permission | v1 | Changes |
+| Landing sample cards | Labelled Sample, done on origin/main; replaced by a real partner finding with permission later | v1 | Stays |
@@ -125 +125 @@ Scoop's line about these pages: I sign the note. Small. Then I vanish.
-3. **Do the landing's sample cards stay?** Proposal: label them Sample this week.
+3. **Do the landing's Sample cards stay, now that they are labelled?** Proposal: keep them until a partner lets us show a real finding.
diff --git a/content/ops-and-infrastructure/protected-connections.md b/content/ops-and-infrastructure/protected-connections.md
index 55b49fe..a21addb 100644
--- a/content/ops-and-infrastructure/protected-connections.md
+++ b/content/ops-and-infrastructure/protected-connections.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main a559de6: connectors/protected.js:1-4, connectors/index.js:70, 148, 269, 429, vault.js:27, tools/prod-env.sh:9, db/connector-ping.mjs:1-24, db/status.mjs:1-15, db/reset-jams.mjs:7, 18-19, db/partner-copy.js:6-8, test/qv1-roles-security.test.js"
+  - "brew origin/main d6154b3: db/brand-wipe.js:8-32, 235, db/delete-brand.mjs:10, test/brand-wipe-pg.test.js:61-79, connectors/protected.js:1-4, connectors/index.js:70, 148, 269, 429, vault.js:27, tools/prod-env.sh:9, db/connector-ping.mjs:1-24, db/status.mjs:1-15, db/reset-jams.mjs:7, 18-19, db/partner-copy.js:6-8, test/qv1-roles-security.test.js"
@@ -49,0 +50,3 @@ If Arata's Shopify token ever starts rotating, production becomes the only refre
+| Brand reset, `db/reset-brand.mjs` | Keeps connections, vault, live numbers and the connected ledger, and fails the run if they changed by a byte | [src: brew/db/brand-wipe.js:15-17, 235] |
+| Data request deletion, `db/delete-brand.mjs` | Refuses any brand in `PROTECTED_CONNECTIONS`; take it out of the setting first, deliberately | [src: brew/db/brand-wipe.js:27; brew/db/delete-brand.mjs:10] |
+| Both brand tools | Refuse the five design partners unless `--partner-override --confirm-slug <slug>` | [src: brew/db/brand-wipe.js:28-30; brew/test/brand-wipe-pg.test.js:61] |
@@ -56 +59,2 @@ If Arata's Shopify token ever starts rotating, production becomes the only refre
-- Bucket scripts such as `tools/reset-brand.sh` delete whole brand folders, vault included, with no protected check; the rule is that they are never run on Arata. [src: brew/tools/reset-brand.sh:6-7] [src: memory brew-live-stores]
+- The bucket scripts `tools/reset-brand.sh` and `tools/flush-live.sh` still have no protected check, but since 26 Oct they exit 3 unless `STORAGE=gcs`, so they cannot run against what the sites read. [src: brew/tools/reset-brand.sh:11-19; brew/tools/flush-live.sh:17-26]
+- A deletion never revokes a platform grant; it prints the grants the brand revokes itself. [src: brew/db/brand-wipe.js:7, 240]
@@ -65 +69 @@ If Arata's Shopify token ever starts rotating, production becomes the only refre
-| Wipe tools that refuse protected brands in code | v1 | New |
+| Wipe tools that refuse protected brands in code, on origin/main since 26 Oct | v1 | Stays |
diff --git a/content/ops-and-infrastructure/restore.md b/content/ops-and-infrastructure/restore.md
index b071d50..7eaf935 100644
--- a/content/ops-and-infrastructure/restore.md
+++ b/content/ops-and-infrastructure/restore.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: tools/promote.sh:54-62, db/connector-ping.mjs:1-24, db/partner-copy.js:1-11, tools/flush-live.sh:6, tools/reset-brand.sh:6-7, db/reset-jams.mjs:4"
+  - "brew origin/main d6154b3: tools/promote.sh:54-62, db/connector-ping.mjs:1-24, db/partner-copy.js:1-11, db/wipe-cli.js:18-20, db/brand-wipe.js:236-238, docs/production.md:108-110, db/reset-jams.mjs:4"
@@ -34 +34,2 @@ This page says what backups exist for each site, how to bring back the whole pro
-| Staging wipes and resets | A copy in `_deleted/<time>/` unless `NO_ARCHIVE=1` | until removed | [src: brew/tools/flush-live.sh:6] [src: brew/tools/reset-brand.sh:6-7] [src: brew/db/reset-jams.mjs:4] |
+| Staging jam and brand resets | A copy in `_deleted/jams-<time>/` or `_deleted/brand-<slug>-<time>/` unless `--no-archive` | until removed | [src: brew/db/reset-jams.mjs:4] [src: brew/db/wipe-cli.js:18-19] |
+| Production brand reset or deletion | No `_deleted/` copy; the database backups hold the brand until they age out | 7 days of logs, 14 daily backups | [src: brew/db/wipe-cli.js:18-20] [src: brew/db/brand-wipe.js:236-238] |
diff --git a/content/ops-and-infrastructure/runbook-index.md b/content/ops-and-infrastructure/runbook-index.md
index f403e29..045c357 100644
--- a/content/ops-and-infrastructure/runbook-index.md
+++ b/content/ops-and-infrastructure/runbook-index.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main a559de6: deploy.sh:1-53, tools/promote.sh:1-119, tools/prod-env.sh:1-13, tools/flush-live.sh:1-44, tools/reset-brand.sh:1-30, tools/reset-jams.sh:1-30, tools/partner-copy.sh:1-22, tools/deploy-demo.sh:1-26, tools/demo-sync.sh:1-30, db/reset-jams.mjs:1-30, db/status.mjs:1-27, db/connector-ping.mjs:1-24, db/parity.mjs:1-30, db/import-bucket.mjs:1-30, db/migrate.js:1-27"
+  - "brew origin/main a559de6: deploy.sh:1-53, tools/promote.sh:1-119, tools/prod-env.sh:1-13, tools/flush-live.sh:1-26, tools/reset-brand.sh:1-20, db/reset-brand.mjs:1-13, db/delete-brand.mjs:1-12, tools/reset-jams.sh:1-30, tools/partner-copy.sh:1-22, tools/deploy-demo.sh:1-26, tools/demo-sync.sh:1-30, db/reset-jams.mjs:1-30, db/status.mjs:1-27, db/connector-ping.mjs:1-24, db/parity.mjs:1-30, db/import-bucket.mjs:1-30, db/migrate.js:1-27"
@@ -11,0 +12 @@ sources:
+  - "brew origin/main d6154b3: docs/production.md:66-110, .github/workflows/test.yml:1-37"
@@ -55 +56,2 @@ This is the list of every operational runbook we have, what each one is for and
-| `tools/flush-live.sh` | Every brand starts over, for a model switch | Staging, bucket era | [Wipe data and reset jams](/ops-and-infrastructure/wipe-data-and-reset-jams) [src: brew/tools/flush-live.sh:1-15] |
+| `db/reset-brand.mjs` through job `brew-ops` | One brand back to a fresh first run, connections kept; also the way to flush every brand, one slug at a time | Staging | [Wipe data and reset jams](/ops-and-infrastructure/wipe-data-and-reset-jams) [src: brew/db/reset-brand.mjs:1-11] |
+| `db/delete-brand.mjs` through job `brew-ops`, or `brew-prod-ops` once made | A brand asks us to delete its data | Staging; production after the next promote | [Data requests](/customer-success-and-support/data-requests) [src: brew/db/delete-brand.mjs:1-10] [src: brew/docs/production.md:95-106] |
@@ -59 +61,8 @@ This is the list of every operational runbook we have, what each one is for and
-- `tools/reset-jams.sh`, `tools/reset-brand.sh` and `tools/flush-live.sh` edit `gs://brew-prototype` directly. Staging has kept its records in Postgres since 25 Oct, so these edit the mirror, not the truth. [src: brew/tools/reset-jams.sh:6] [src: brew/tools/reset-brand.sh:12] [src: brew/tools/flush-live.sh:18] [src: memory brew-jam-reset]
+- `tools/reset-jams.sh` edits `gs://brew-prototype` directly. Staging has kept its records in Postgres since 25 Oct, so it edits the mirror, not the truth. [src: brew/tools/reset-jams.sh:6] [src: memory brew-jam-reset]
+- `tools/reset-brand.sh` and `tools/flush-live.sh` are bucket era too, but since 26 Oct they exit 3 unless `STORAGE=gcs` and print the `brew-ops` command to run instead. [src: brew/tools/reset-brand.sh:11-19] [src: brew/tools/flush-live.sh:17-26]
+
+### Checking
+
+| Runbook | Use it when | Site | Where it is written |
+|---|---|---|---|
+| GitHub Actions workflow `test` | Every push and pull request runs the deploy test gate on Node 22 with Postgres 17 | GitHub | [Testing and QA](/engineering/testing-and-qa) [src: brew/.github/workflows/test.yml:1-37] |
@@ -66,2 +75,3 @@ This is the list of every operational runbook we have, what each one is for and
-| A Postgres version of reset-brand and flush, run as `brew-ops` jobs | v1 | New |
-| Retire the three bucket scripts once their Postgres versions exist | v1 | Goes |
+| A Postgres brand reset and deletion, run as `brew-ops` jobs, on origin/main | v1 | Stays |
+| A `brew-prod-ops` job on production for deletions | v1 | New |
+| Retire the three bucket scripts; two already refuse on Postgres | v1 | Goes |
@@ -89 +99 @@ git -C /Users/param/Documents/ChatGPT/brew worktree add --detach "$TMPDIR/brew-o
-| Stale runbooks | Scripts that target storage the site no longer uses | This page, 3 today |
+| Stale runbooks | Scripts that target storage the site no longer uses and still run | This page, 1 today, `tools/reset-jams.sh` |
@@ -94 +104 @@ git -C /Users/param/Documents/ChatGPT/brew worktree add --detach "$TMPDIR/brew-o
-2. **Should the three bucket scripts be deleted now?** Proposal: keep them, add a first line that stops when the service runs `STORAGE=pg`, and write the Postgres versions next.
+2. **Should the bucket scripts be deleted now?** `tools/reset-brand.sh` and `tools/flush-live.sh` already stop on `STORAGE=pg`. Proposal: give `tools/reset-jams.sh` the same first lines, and delete all three once production has run on Postgres for a month.
diff --git a/content/ops-and-infrastructure/wipe-data-and-reset-jams.md b/content/ops-and-infrastructure/wipe-data-and-reset-jams.md
index 2780e32..112bd0f 100644
--- a/content/ops-and-infrastructure/wipe-data-and-reset-jams.md
+++ b/content/ops-and-infrastructure/wipe-data-and-reset-jams.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10,2 @@ sources:
-  - "brew origin/main a559de6: db/reset-jams.mjs:1-30, tools/reset-jams.sh:1-30, tools/reset-brand.sh:1-30, tools/flush-live.sh:1-44, retire.js:1-40, deploy.sh:30-32, tools/prod-env.sh:9"
+  - "brew origin/main d6154b3: db/reset-jams.mjs:1-30, db/reset-brand.mjs:1-13, db/delete-brand.mjs:1-12, db/brand-wipe.js:1-32, 155-185, 214-241, db/wipe-cli.js:9-22, tools/reset-jams.sh:1-30, tools/reset-brand.sh:1-20, tools/flush-live.sh:1-26, retire.js:1-40, tools/prod-env.sh:9, docs/production.md:66-110, test/brand-wipe-pg.test.js:46-142"
+  - "brew commit e3e0fb4; deploy state 26 Oct: staging deploying, production unchanged"
@@ -18 +19 @@ sources:
-There are three ways to start over on staging, a jam reset, a brand wipe and a full flush, and this page says what each one removes, what it never touches and which of them still work now that staging keeps its records in Postgres.
+There are three ways to start over, a jam reset, a brand reset and a full flush, plus a deletion for a data request; this page says what each removes, what it never touches and which tool to run now that both sites keep their records in Postgres.
@@ -24,9 +25,7 @@ There are three ways to start over on staging, a jam reset, a brand wipe and a f
-| | Jam reset | Brand wipe, "wipe data" | Full flush |
-|---|---|---|---|
-| Tool | `db/reset-jams.mjs` as job `brew-ops` | `tools/reset-brand.sh` plus the steps in memory brew-wipe-data | `tools/flush-live.sh` [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
-| Works on | Postgres rows | the bucket `gs://brew-prototype` | the bucket `gs://brew-prototype` [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
-| Removes | v1 jams, Studio jams, jam and remix state in workspaces | the brand folder: read, Plays, members, vault sign-ins, connections, jams; its workspaces; then the treg and Apify cache, made media, research media, saved video reads | every brand, every workspace, made media, research media [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
-| Keeps | cards, Plays, drafts, learnings, connections | `brands/_brew/` counters and calendar, our own Sloosh connection, `control/`, `backups/`, the demo bucket | `brands/_brew/`, `_cache/`, `control/live.json`, `backups/`, the demo bucket [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
-| Never touches | `private/connections.json`, `private/vault/`, `live/`, `private/live/`; a test pins this | provider-side grants at Shopify, Meta, Google, Slack | the same [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
-| Copy kept | `_deleted/jams-<time>/` | `_deleted/<time>/` and `_deleted/<time>-cache/` | `_deleted/<time>/` unless `NO_ARCHIVE=1` [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
-| Offline time | about a minute per brand | about a minute | about a minute [src: brew/db/reset-jams.mjs:1-19] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15] [src: memory brew-wipe-data] |
+| | Jam reset | Brand reset, "wipe data" | Data request deletion | Full flush |
+|---|---|---|---|---|
+| Tool | `db/reset-jams.mjs` as job `brew-ops` [src: brew/db/reset-jams.mjs:1-19] | `db/reset-brand.mjs` as job `brew-ops` [src: brew/db/reset-brand.mjs:1-4] | `db/delete-brand.mjs` as job `brew-ops`, or `brew-prod-ops` on production [src: brew/db/delete-brand.mjs:1-4] | `tools/flush-live.sh`, bucket era, refuses on Postgres [src: brew/tools/flush-live.sh:17-26] |
+| Removes | v1 jams, Studio jams, jam and remix state in workspaces [src: brew/db/reset-jams.mjs:1-19] | rows under `brands/<slug>/`, the brand's workspaces, build progress, its queued and finished jobs, bus messages, the brand folder's bucket files [src: brew/db/reset-brand.mjs:5-6] | all of the brand: rows with our stored tokens, workspaces, `_deleted/` copies, build progress, jobs, bus, counters, access log, every bucket version, media only it points at [src: brew/db/delete-brand.mjs:5-7] | every brand, every workspace, made and research media [src: brew/tools/flush-live.sh:6-9] |
+| Keeps | cards, Plays, drafts, learnings, connections [src: brew/db/reset-jams.mjs:1-19] | `private/connections.json`, `private/vault/`, `live/`, `private/live/`, `private/live-base.json`, `private/connected-ledger.json`, checked byte for byte afterwards [src: brew/db/reset-brand.mjs:8-9; brew/db/brand-wipe.js:17, 235] | one `data_deletions` row [src: brew/db/delete-brand.mjs:7-8] | `brands/_brew/`, `_cache/`, `control/live.json`, `backups/` [src: brew/tools/flush-live.sh:10-12] |
+| Copy kept | `_deleted/jams-<time>/` [src: memory brew-jam-reset] | `_deleted/brand-<slug>-<time>/` on staging, none on production [src: brew/db/wipe-cli.js:18-20] | none [src: brew/db/wipe-cli.js:19] | `_deleted/<time>/` unless `NO_ARCHIVE=1` [src: brew/tools/flush-live.sh:6] |
+| Never calls a platform | yes | yes | yes, and prints the grants the brand revokes itself [src: brew/db/brand-wipe.js:7, 240] | yes |
@@ -34 +33,5 @@ There are three ways to start over on staging, a jam reset, a brand wipe and a f
-Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [src: memory brew-wipe-data] [src: brew/tools/reset-brand.sh:1-9] [src: brew/tools/flush-live.sh:6-15]
+- Both brand tools are dry runs until `--yes`, and print every row, workspace, job and file they would remove. [src: brew/db/brand-wipe.js:2, 135-149, 218]
+- Both refuse while a job for the brand is running. [src: brew/db/brand-wipe.js:219]
+- `tools/reset-brand.sh` and `tools/flush-live.sh` now exit 3 unless `STORAGE=gcs`, and print the `brew-ops` commands instead. [src: brew/tools/reset-brand.sh:11-19; brew/tools/flush-live.sh:17-26]
+- There is no Postgres full flush. A flush is a brand reset per slug. [src: brew/tools/flush-live.sh:21; brew/docs/production.md:73-77]
+- The brand reset does not touch the treg and Apify cache or made media, which the "wipe data" steps also clear. [src: brew/db/reset-brand.mjs:5-6; memory brew-wipe-data]
@@ -40 +43,2 @@ Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [
-- On Postgres the marker is a row, not a bucket file. [src: brew/retire.js:29-30]
+- On Postgres the marker is a row. The brand tools write it themselves, which is why staging needs `--rev-n <n>`. [src: brew/retire.js:29-30; brew/db/brand-wipe.js:220-225]
+- Production sets `RETIRE_OFF=1`, so there is no marker: the tool removes, waits and sweeps three times for anything an instance wrote back, then verifies. [src: brew/tools/prod-env.sh:9; brew/db/brand-wipe.js:228-234; brew/docs/production.md:95-96]
@@ -42 +46 @@ Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [
-### Which still work
+### Who may be reset or deleted
@@ -44,4 +48,4 @@ Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [
-- **Jam reset works.** It was run on 25 Oct across 14 brands and 91 workspaces; Arata's Meta and Shopify stayed live. [src: memory brew-jam-reset]
-- **`tools/reset-jams.sh` no longer works.** It edits bucket files; staging runs on Postgres. [src: memory brew-jam-reset] [src: brew/tools/reset-jams.sh:6]
-- **`tools/reset-brand.sh` and `tools/flush-live.sh` are in the same position.** They write the stop marker and delete under `gs://brew-prototype`, while staging reads its records and its marker from Postgres. Nothing we read says they were updated or rerun since 25 Oct. [src: brew/tools/reset-brand.sh:12] [src: brew/tools/reset-brand.sh:18] [src: brew/tools/flush-live.sh:18] [src: brew/retire.js:29-30]
-- **Production has no wipe tool.** Every script names staging, and production sets `RETIRE_OFF=1`. [src: brew/tools/reset-brand.sh:12] [src: brew/tools/prod-env.sh:9]
+- The five design partners, Arata, Nutrova, Mana, PUMA India and Scrubroom, are refused in code unless `--partner-override --confirm-slug <slug>` is given. Only if the partner asked. [src: brew/db/brand-wipe.js:28-30; memory brew-live-stores]
+- A brand in `PROTECTED_CONNECTIONS`, Arata by default, is never deleted by the tool. A reset keeps its connections. [src: brew/db/brand-wipe.js:27; brew/connectors/protected.js:3]
+- Risky tests run on Cozy Snugglet or Zenant. [src: memory brew-live-stores]
+- Tests cover a dry run, the partner refusal, a reset that leaves every connection as it was, the stop marker, a deletion with its audit row, and the bucket era scripts refusing. [src: brew/test/brand-wipe-pg.test.js:46-142]
@@ -49 +53 @@ Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [
-### Who may be wiped
+### Where it runs
@@ -51,3 +55,2 @@ Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [
-- Never the five design partners: Arata, Nutrova, Mana, PUMA India, Scrubroom. [src: memory brew-live-stores]
-- Risky tests run on Cozy Snugglet or Zenant. A "wipe everything else" lists the slugs first. [src: memory brew-live-stores]
-- Arata's Meta and Shopify are never disconnected by any wipe. [src: memory never-disconnect-arata-meta]
+- Staging: `brew-ops`, with the code on origin/main once this deploy lands. [src: brew/docs/production.md:83-93; deploy state 26 Oct]
+- Production: no `brew-prod-ops` job yet, and the `data_deletions` table arrives with the next promote. [src: brew/docs/production.md:95-106; deploy state 26 Oct]
@@ -60,4 +63,5 @@ Sources: [src: brew/db/reset-jams.mjs:1-7] [src: brew/db/reset-jams.mjs:18-19] [
-| A Postgres brand wipe, `db/reset-brand.mjs`, with the same archive and stop marker | v1 | New |
-| A Postgres full flush built on it | v1 | New |
-| Bucket wipe scripts | v1 | Goes, once the Postgres ones exist |
-| Wipes that refuse the five partners and any protected brand in code | v1 | New |
+| Postgres brand reset, `db/reset-brand.mjs`, with archive, stop marker and partner refusal | v1 | Stays |
+| Data request deletion, `db/delete-brand.mjs`, with an audit row | v1 | Stays |
+| A `brew-prod-ops` job on production, made once after the next promote | v1 | New |
+| A cache and media sweep for "wipe data" on Postgres | v1 | New, proposed |
+| Bucket wipe scripts | v1 | Goes; they already refuse on Postgres |
@@ -86,4 +90,14 @@ When Param says "wipe data", every step runs, without asking. [src: memory brew-
-2. Wipe the brand and its workspaces, with a copy in `_deleted/`. Include old or misspelt slugs.
-3. Archive, then delete the treg and Apify cache, made media, research media and saved video reads.
-4. Restart the live instance with a stop marker and an empty `wipe` list.
-5. Verify: `/api/brands/<slug>/status` says unknown brand, landing 200, busy 0.
+2. Dry run the brand reset and read every line:
+
+   ```bash
+   gcloud run jobs execute brew-ops --region=asia-south1 --args=db/reset-brand.mjs,--slug,<slug> --wait
+   ```
+
+3. Run it, with `n` from `shopos-feed-<n>-xxx`. It hides the brand, removes it, sweeps three times and lifts the hide. Repeat for old or misspelt slugs.
+
+   ```bash
+   gcloud run jobs execute brew-ops --region=asia-south1 --args=db/reset-brand.mjs,--slug,<slug>,--rev-n,<n>,--yes --wait
+   ```
+
+4. The treg and Apify cache, made media and saved video reads have no Postgres tool yet; stop and ask Param before touching them.
+5. Verify: the run ends with `VERIFY: <slug> holds nothing to reset any more` and the kept connection rows unchanged; landing 200, busy 0.
@@ -92 +106,5 @@ When Param says "wipe data", every step runs, without asking. [src: memory brew-
-Until the Postgres brand wipe exists, steps 2 and 4 have no working tool on staging; stop and ask Param.
+[src: brew/docs/production.md:83-93; brew/db/brand-wipe.js:234-235]
+
+### A data request
+
+Follow data requests, 12.9. The tool is `db/delete-brand.mjs`, dry run first.
@@ -98,2 +116,3 @@ Until the Postgres brand wipe exists, steps 2 and 4 have no working tool on stag
-| Resets run | Jam resets and wipes, with date, site and brands | Proposed: one line each in this page |
-| Protected brands touched | Wipes that changed a partner or Arata's connections | Proposed; must stay 0 |
+| Resets run | Jam and brand resets, with date, site and brands | Proposed: one line each in this page |
+| Deletions run | Data request deletions, finished and unfinished | the `data_deletions` table, `done_at` |
+| Protected brands touched | Runs that changed a partner or Arata's connections | Proposed; must stay 0 |
@@ -103,2 +122,2 @@ Until the Postgres brand wipe exists, steps 2 and 4 have no working tool on stag
-1. **Build the Postgres brand wipe now?** Proposal: yes, as `db/reset-brand.mjs` run by `brew-ops`, copying the jam reset's archive, marker and protected-path guard, and refusing the five partners unless a `--partner` word is given.
-2. **May anyone ever wipe on production?** Proposal: no wipe tool for production; a partner's "Delete my data" request is done by Param by hand with a backup first, until v2.
+1. **May a brand reset run on production?** The code allows it, with no archive. Proposal: no; production runs only `db/delete-brand.mjs` for a data request.
+2. **What clears the cache and made media on Postgres?** Proposal: add it to `db/reset-brand.mjs` behind a `--with-cache` flag, so "wipe data" is one job.
diff --git a/content/pr-and-communications/crisis-communications.md b/content/pr-and-communications/crisis-communications.md
index 3f3f3bc..aa8630f 100644
--- a/content/pr-and-communications/crisis-communications.md
+++ b/content/pr-and-communications/crisis-communications.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -12 +12 @@ sources:
-  - "brew origin/main: docs/production.md:66-69; tools/prod-env.sh:9; legal-pages.js:33"
+  - "brew origin/main: docs/production.md:112-115; tools/prod-env.sh:9; legal-pages.js:33"
@@ -30 +30 @@ We have no crisis plan: no source says who speaks, how fast, or what counts as a
-| Alerts reach only us: Cloud Monitoring and the app's daily alerts email param@ and hello@ | [src: brew/docs/production.md:66-69] |
+| Alerts reach only us: Cloud Monitoring and the app's daily alerts email param@ and hello@ | [src: brew/docs/production.md:112-115] |
diff --git a/content/pr-and-communications/internal-updates.md b/content/pr-and-communications/internal-updates.md
index bd59705..49da479 100644
--- a/content/pr-and-communications/internal-updates.md
+++ b/content/pr-and-communications/internal-updates.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -12 +12 @@ sources:
-  - "brew origin/main: docs/production.md:66-69"
+  - "brew origin/main: docs/production.md:112-115"
@@ -35 +35 @@ The team stays in the loop through this handbook and its tools, not a separate n
-| Alerts | Uptime and app alerts email param@ and hello@ | Param | [src: brew/docs/production.md:66-69] |
+| Alerts | Uptime and app alerts email param@ and hello@ | Param | [src: brew/docs/production.md:112-115] |
diff --git a/content/product-marketing/release-notes.md b/content/product-marketing/release-notes.md
index ab4073c..03a9654 100644
--- a/content/product-marketing/release-notes.md
+++ b/content/product-marketing/release-notes.md
@@ -10 +10 @@ sources:
-  - git log brew origin/main a559de6
+  - git log brew origin/main 45b6a06
diff --git a/content/product/features-a-to-z.md b/content/product/features-a-to-z.md
index 7de4cea..80dd3e3 100644
--- a/content/product/features-a-to-z.md
+++ b/content/product/features-a-to-z.md
@@ -11 +11 @@ sources:
-  - brew origin/main a559de6
+  - brew origin/main 45b6a06
@@ -269 +269 @@ Every feature of Brew, A to Z: 244 items, with where each stands today and where
-[src: brew origin/main a559de6]
+[src: brew origin/main 45b6a06]
diff --git a/content/product/the-loop-step-by-step.md b/content/product/the-loop-step-by-step.md
index 28733e4..425e3b9 100644
--- a/content/product/the-loop-step-by-step.md
+++ b/content/product/the-loop-step-by-step.md
@@ -26 +26 @@ The loop that runs every week, with every item in it: evidence, plays, cards, th
-We read your store and 67 sources around it, and keep reading, so every idea starts from something real.
+We read your store and what is around it, 68 sources in all, 67 read today, and keep reading, so every idea starts from something real.
@@ -427 +427 @@ Tell us what happened, drop in a deck or just keep working, and the plays and ca
-[src: brew origin/main a559de6]
+[src: brew origin/main 45b6a06]
diff --git a/content/sales-and-partnerships/demo-script.bee.md b/content/sales-and-partnerships/demo-script.bee.md
index 6509359..bf1ff2a 100644
--- a/content/sales-and-partnerships/demo-script.bee.md
+++ b/content/sales-and-partnerships/demo-script.bee.md
@@ -9 +9 @@ direction: bee
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/demo-picker.html:154-201, public/app.js:276-288, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
+  - "brew origin/main d6154b3: public/demo-picker.html:154-201, public/app.js:276-290, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
@@ -34 +34 @@ The five minute demo in the Buzz direction: open a prepared store on the demo si
-| The picker's hint names the product and says moves: `From the first screen: Brew reads the store live, you check what it found, the moves are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] |
+| The picker's hint, since 26 Oct: `From the first screen: we read the store live, you check what we found, the plays are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] [src: brew commit ca2bc7b] |
@@ -41 +41 @@ The five minute demo in the Buzz direction: open a prepared store on the demo si
-| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-288] [src: memory brew-access-rules] |
+| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-290] [src: memory brew-access-rules] |
@@ -91 +91 @@ The five minute demo in the Buzz direction: open a prepared store on the demo si
-| The picker hint says Plays and never names the product | v1 | Changes |
+| The picker hint says plays and never names the product, done on origin/main | v1 | Stays |
@@ -171 +170,0 @@ The five minute demo in the Buzz direction: open a prepared store on the demo si
-5. **Who fixes the picker hint?** Proposal: change it to "Reads the store live, you check what it found, the Plays are ready. About 4 minutes." in the next release.
diff --git a/content/sales-and-partnerships/demo-script.cooker.md b/content/sales-and-partnerships/demo-script.cooker.md
index fffdf27..0fdf3c9 100644
--- a/content/sales-and-partnerships/demo-script.cooker.md
+++ b/content/sales-and-partnerships/demo-script.cooker.md
@@ -9 +9 @@ direction: cooker
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/demo-picker.html:154-201, public/app.js:276-288, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
+  - "brew origin/main d6154b3: public/demo-picker.html:154-201, public/app.js:276-290, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
@@ -34 +34 @@ The five minute demo in the Seeti direction: open a prepared store on the demo s
-| The picker's hint names the product and says moves: `From the first screen: Brew reads the store live, you check what it found, the moves are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] |
+| The picker's hint, since 26 Oct: `From the first screen: we read the store live, you check what we found, the plays are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] [src: brew commit ca2bc7b] |
@@ -41 +41 @@ The five minute demo in the Seeti direction: open a prepared store on the demo s
-| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-288] [src: memory brew-access-rules] |
+| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-290] [src: memory brew-access-rules] |
@@ -91 +91 @@ The five minute demo in the Seeti direction: open a prepared store on the demo s
-| The picker hint says Plays and never names the product | v1 | Changes |
+| The picker hint says plays and never names the product, done on origin/main | v1 | Stays |
@@ -171 +170,0 @@ The five minute demo in the Seeti direction: open a prepared store on the demo s
-5. **Who fixes the picker hint?** Proposal: change it to "Reads the store live, you check what it found, the Plays are ready. About 4 minutes." in the next release.
diff --git a/content/sales-and-partnerships/demo-script.dog.md b/content/sales-and-partnerships/demo-script.dog.md
index 4609ed6..4071412 100644
--- a/content/sales-and-partnerships/demo-script.dog.md
+++ b/content/sales-and-partnerships/demo-script.dog.md
@@ -9 +9 @@ direction: dog
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/demo-picker.html:154-201, public/app.js:276-288, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
+  - "brew origin/main d6154b3: public/demo-picker.html:154-201, public/app.js:276-290, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
@@ -34 +34 @@ The five minute demo in the Fetch direction: open a prepared store on the demo s
-| The picker's hint names the product and says moves: `From the first screen: Brew reads the store live, you check what it found, the moves are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] |
+| The picker's hint, since 26 Oct: `From the first screen: we read the store live, you check what we found, the plays are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] [src: brew commit ca2bc7b] |
@@ -41 +41 @@ The five minute demo in the Fetch direction: open a prepared store on the demo s
-| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-288] [src: memory brew-access-rules] |
+| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-290] [src: memory brew-access-rules] |
@@ -91 +91 @@ The five minute demo in the Fetch direction: open a prepared store on the demo s
-| The picker hint says Plays and never names the product | v1 | Changes |
+| The picker hint says plays and never names the product, done on origin/main | v1 | Stays |
@@ -171 +170,0 @@ The five minute demo in the Fetch direction: open a prepared store on the demo s
-5. **Who fixes the picker hint?** Proposal: change it to "Reads the store live, you check what it found, the Plays are ready. About 4 minutes." in the next release.
diff --git a/content/sales-and-partnerships/demo-script.octopus.md b/content/sales-and-partnerships/demo-script.octopus.md
index f6c6a4f..5e2b38a 100644
--- a/content/sales-and-partnerships/demo-script.octopus.md
+++ b/content/sales-and-partnerships/demo-script.octopus.md
@@ -9 +9 @@ direction: octopus
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/demo-picker.html:154-201, public/app.js:276-288, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
+  - "brew origin/main d6154b3: public/demo-picker.html:154-201, public/app.js:276-290, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
@@ -34 +34 @@ The five minute demo in the Ocho direction: open a prepared store on the demo si
-| The picker's hint names the product and says moves: `From the first screen: Brew reads the store live, you check what it found, the moves are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] |
+| The picker's hint, since 26 Oct: `From the first screen: we read the store live, you check what we found, the plays are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] [src: brew commit ca2bc7b] |
@@ -41 +41 @@ The five minute demo in the Ocho direction: open a prepared store on the demo si
-| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-288] [src: memory brew-access-rules] |
+| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-290] [src: memory brew-access-rules] |
@@ -91 +91 @@ The five minute demo in the Ocho direction: open a prepared store on the demo si
-| The picker hint says Plays and never names the product | v1 | Changes |
+| The picker hint says plays and never names the product, done on origin/main | v1 | Stays |
@@ -171 +170,0 @@ The five minute demo in the Ocho direction: open a prepared store on the demo si
-5. **Who fixes the picker hint?** Proposal: change it to "Reads the store live, you check what it found, the Plays are ready. About 4 minutes." in the next release.
diff --git a/content/sales-and-partnerships/demo-script.raccoon.md b/content/sales-and-partnerships/demo-script.raccoon.md
index 33cb7f5..820045b 100644
--- a/content/sales-and-partnerships/demo-script.raccoon.md
+++ b/content/sales-and-partnerships/demo-script.raccoon.md
@@ -9 +9 @@ direction: raccoon
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew origin/main a559de6: public/demo-picker.html:154-201, public/app.js:276-288, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
+  - "brew origin/main d6154b3: public/demo-picker.html:154-201, public/app.js:276-290, DUMMY.md:14-21, docs/handover/product/08-demo-guide.md:1-47"
@@ -34 +34 @@ The five minute demo in the Scoop direction: open a prepared store on the demo s
-| The picker's hint names the product and says moves: `From the first screen: Brew reads the store live, you check what it found, the moves are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] |
+| The picker's hint, since 26 Oct: `From the first screen: we read the store live, you check what we found, the plays are ready. About 4 minutes.` | [src: brew/public/demo-picker.html:186] [src: brew commit ca2bc7b] |
@@ -41 +41 @@ The five minute demo in the Scoop direction: open a prepared store on the demo s
-| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-288] [src: memory brew-access-rules] |
+| Outsiders who sign in on the live site with a personal email are pointed to the demo stores | [src: brew/public/app.js:276-290] [src: memory brew-access-rules] |
@@ -91 +91 @@ The five minute demo in the Scoop direction: open a prepared store on the demo s
-| The picker hint says Plays and never names the product | v1 | Changes |
+| The picker hint says plays and never names the product, done on origin/main | v1 | Stays |
@@ -171 +170,0 @@ The five minute demo in the Scoop direction: open a prepared store on the demo s
-5. **Who fixes the picker hint?** Proposal: change it to "Reads the store live, you check what it found, the Plays are ready. About 4 minutes." in the next release.
diff --git a/content/sales-and-partnerships/onboarding-playbook.md b/content/sales-and-partnerships/onboarding-playbook.md
index 6507428..16df64f 100644
--- a/content/sales-and-partnerships/onboarding-playbook.md
+++ b/content/sales-and-partnerships/onboarding-playbook.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew origin/main a559de6: auth.js:78-110, build.js:93, 239, 363 and 1070, confirm-remind.js:10-33 and 114-122, public/app.js:78-90 and 276-288, docs/connectors-approvals.md:6-37, docs/production.md:71-81, connectors/protected.js:3"
+  - "brew origin/main a559de6: auth.js:78-110, build.js:93, 239, 363 and 1070, confirm-remind.js:10-33 and 114-122, public/app.js:78-90 and 276-288, docs/connectors-approvals.md:6-37, docs/production.md:117-127, connectors/protected.js:3"
@@ -66 +66 @@ The steps from a brand saying yes to its first win, which we define as the first
-- Sloosh needs a staff sign in on each site before finals work there [src: brew/docs/production.md:79-80]
+- Sloosh needs a staff sign in on each site before finals work there [src: brew/docs/production.md:125-126]
diff --git a/content/security-privacy-and-legal/incident-response.md b/content/security-privacy-and-legal/incident-response.md
index 30cbf9d..b5ab418 100644
--- a/content/security-privacy-and-legal/incident-response.md
+++ b/content/security-privacy-and-legal/incident-response.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew/docs/production.md:31, 45-57, 66-69"
+  - "brew/docs/production.md:31, 45-57, 112-115"
@@ -28 +28 @@ No incident or breach runbook exists; this document proposes one, the steps, who
-| Cloud Monitoring emails param@ and hello@ on errors, slow requests, a lost worker lease, failed releases, disk, connections, CPU, export failure and site down | [src: brew/docs/production.md:66-69] |
+| Cloud Monitoring emails param@ and hello@ on errors, slow requests, a lost worker lease, failed releases, disk, connections, CPU, export failure and site down | [src: brew/docs/production.md:112-115] |
diff --git a/content/security-privacy-and-legal/platform-compliance.md b/content/security-privacy-and-legal/platform-compliance.md
index 9ff7db4..289004b 100644
--- a/content/security-privacy-and-legal/platform-compliance.md
+++ b/content/security-privacy-and-legal/platform-compliance.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -13 +13 @@ sources:
-  - "brew/docs/production.md:71-81"
+  - "brew/docs/production.md:117-127"
@@ -49 +49 @@ Where each platform's review stands, Shopify, Meta, Google and Slack, what each
-- The Shopify app's URLs, redirects and webhooks point at staging, brew.joinsquareup.com; production waits for the review. [src: brew/docs/shopify.app.toml:8, 19-31] [src: brew/docs/production.md:79-81]
+- The Shopify app's URLs, redirects and webhooks point at staging, brew.joinsquareup.com; production waits for the review. [src: brew/docs/shopify.app.toml:8, 19-31] [src: brew/docs/production.md:125-127]
diff --git a/content/security-privacy-and-legal/retention-and-deletion.md b/content/security-privacy-and-legal/retention-and-deletion.md
index 68ba5e8..80850ab 100644
--- a/content/security-privacy-and-legal/retention-and-deletion.md
+++ b/content/security-privacy-and-legal/retention-and-deletion.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -11 +11 @@ sources:
-  - "brew/auth.js:78, 101, 224, 244"
+  - "brew/auth.js:79, 102, 225, 245"
@@ -14 +14 @@ sources:
-  - "brew/shopify-app.js:15-19, 30, 203-217"
+  - "brew/shopify-app.js:18-19, 31, 204-218"
@@ -17,2 +17,7 @@ sources:
-  - "brew/legal-pages.js:33"
-  - "brew/tools/reset-brand.sh:1-13"
+  - "brew/legal-pages.js:34"
+  - "brew/tools/reset-brand.sh:11-19"
+  - "brew/db/delete-brand.mjs:1-10"
+  - "brew/db/brand-wipe.js:5-9, 27-30, 74-84, 187-195, 206-209, 236-240"
+  - "brew/db/migrations/010_data_deletions.sql:1-18"
+  - "brew/docs/production.md:95-110"
+  - "brew commit e3e0fb4; deploy state 26 Oct"
@@ -38,2 +43,2 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-| Session cookie `fs_s` | The person's browser | 30 days | [src: brew/auth.js:244] |
-| Sign in handshake `fs_g` | The person's browser | 10 minutes | [src: brew/auth.js:224] |
+| Session cookie `fs_s` | The person's browser | 30 days | [src: brew/auth.js:245] |
+| Sign in handshake `fs_g` | The person's browser | 10 minutes | [src: brew/auth.js:225] |
@@ -42 +47 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-| Invites | `private/members.json` | Count for 14 days; up to 50 kept | [src: brew/auth.js:78, 101, 186] |
+| Invites | `private/members.json` | Count for 14 days; up to 50 kept | [src: brew/auth.js:79, 102, 187] |
@@ -47,2 +52,3 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-| A Shopify store's key and numbers | Vault and live rows | Dropped on `shop/redact`, 48 hours after uninstall, unless installed again | [src: brew/shopify-app.js:30, 203-214] |
-| Per customer Shopify records | None kept | Nothing to keep | [src: brew/shopify-app.js:15-19, 217] |
+| A Shopify store's key and numbers | Vault and live rows | Dropped on `shop/redact`, 48 hours after uninstall, unless installed again | [src: brew/shopify-app.js:31, 204-216] |
+| Per customer Shopify records | None kept | Nothing to keep | [src: brew/shopify-app.js:18-19, 218] |
+| A record of each deletion | `data_deletions` table: slug, request, operator, times, counts by folder, grants, what backups still hold; never contents or member names | No end date; on staging now, production with the next promote | [src: brew/db/migrations/010_data_deletions.sql:1-18] [src: brew/docs/production.md:95-97] |
@@ -58,3 +64,8 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-- The privacy page names what goes, the read, Plays, jams and connected keys, with no time. [src: brew/legal-pages.js:33]
-- No deletion job exists for production; a brand on production is deleted by hand. [src: handbook content/customer-success-and-support/data-requests.md]
-- `tools/reset-brand.sh` wipes a brand from the staging bucket and archives a copy in `_deleted/`; it does not touch production or Postgres. [src: brew/tools/reset-brand.sh:1-13]
+- The privacy page names what goes, the read, Plays, jams and connected keys, with no time. [src: brew/legal-pages.js:34]
+- `db/delete-brand.mjs` deletes a brand for a data request on a Postgres site: every row under the brand with our stored tokens, workspaces, `_deleted/` copies, build progress, jobs, bus, counters, access log rows, every bucket version of its files, and media only it points at. Dry run until `--yes`. [src: brew/db/delete-brand.mjs:1-8; brew/db/brand-wipe.js:5-6]
+- It keeps no archive, only the `data_deletions` row, and it writes that row before it starts, so an unfinished run shows. [src: brew/db/brand-wipe.js:187-195, 222]
+- File deletes remove every old version too, so object versioning keeps no copy. [src: brew/db/brand-wipe.js:206-209]
+- What still holds the brand for a while is printed for the reply: 7 days of point in time logs, 14 daily backups, the nightly export, and the bucket's 7 day soft delete. [src: brew/db/brand-wipe.js:236-238]
+- It refuses the five partners unless `--partner-override --confirm-slug <slug>`, and never deletes a brand in `PROTECTED_CONNECTIONS`. [src: brew/db/brand-wipe.js:27-30]
+- Production has no `brew-prod-ops` job and no `data_deletions` table yet; both come with the next promote. Until then a brand on production is deleted by hand. [src: brew/docs/production.md:95-106; deploy state 26 Oct]
+- `tools/reset-brand.sh` is bucket era and exits 3 on Postgres. [src: brew/tools/reset-brand.sh:11-19]
@@ -62 +73 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-- Disconnecting inside the product never revokes the grant on Shopify, Meta, Google or Slack. [src: memory brew-wipe-data]
+- Disconnecting inside the product, and the deletion tool, never revoke the grant on Shopify, Meta, Google, Slack or Klaviyo; the tool prints where the brand revokes each one. [src: memory brew-wipe-data] [src: brew/db/brand-wipe.js:74-84, 240]
@@ -74 +85 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-| Deletion archive | 30 days, then purged | v1 | New |
+| Deletion audit row | Kept as proof of the deletion; no contents | v1 | Stays |
@@ -85 +96 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-| `db/delete-brand.mjs`: a dry run, then removes one brand's rows, parts, files and keys, archives for 30 days | v1 | New, proposed |
+| `db/delete-brand.mjs`: a dry run, then removes one brand's rows, parts, files and keys, with an audit row and no archive. On origin/main; production after the next promote | v1 | Stays |
@@ -93 +104 @@ What we keep, for how long and how it is deleted; today most brand data is kept
-3. After a deletion, tell the person to revoke our grant on Shopify, Meta, Google or Slack if they want that too.
+3. After a deletion, send the person the grants the run printed, to revoke themselves, and what backups still hold for up to 14 days.
@@ -113,3 +124,2 @@ gcloud run jobs execute brew-ops --region=asia-south1 --wait
-2. **Build the production deletion job now?** Proposal: yes, before the first brand outside the five partners, with a dry run first.
-3. **How long do we keep the access log?** Proposal: 12 months.
-4. **Do restored backups bring deleted brands back?** Proposal: keep a list of deleted slugs and delete them again after any restore.
+2. **How long do we keep the access log?** Proposal: 12 months.
+3. **Do restored backups bring deleted brands back?** Proposal: keep a list of deleted slugs and delete them again after any restore.
diff --git a/content/security-privacy-and-legal/security-practices.md b/content/security-privacy-and-legal/security-practices.md
index 7bc11c3..d2c93c5 100644
--- a/content/security-privacy-and-legal/security-practices.md
+++ b/content/security-privacy-and-legal/security-practices.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10,2 @@ sources:
-  - "brew/auth.js:3-5, 45-46, 53-63, 65, 72, 220-246, 249"
+  - "brew origin/main d6154b3: .github/workflows/test.yml:1-16, db/brand-wipe.js:7-32, 187-195, docs/production.md:112-115"
+  - "brew/auth.js:4-6, 46-47, 54-64, 66, 73, 221-247, 250"
@@ -18 +19 @@ sources:
-  - "brew/docs/production.md:9-15, 25-33, 35-45, 66-69"
+  - "brew/docs/production.md:9-15, 25-33, 35-45, 112-115"
@@ -35,3 +36,3 @@ How we keep brand data safe today, sign in, sessions, secrets, least access, the
-- Google sign in with PKCE and a nonce; the id token's audience, issuer, expiry, nonce and verified email are checked. [src: brew/auth.js:3-4, 220-238]
-- Sessions are an HMAC signed cookie, HttpOnly, SameSite Lax, Secure on Cloud Run, for 30 days. [src: brew/auth.js:53-63, 65, 244]
-- Every token carries its type, so a connect state never works as a session. [src: brew/auth.js:55-63]
+- Google sign in with PKCE and a nonce; the id token's audience, issuer, expiry, nonce and verified email are checked. [src: brew/auth.js:4-5, 221-239]
+- Sessions are an HMAC signed cookie, HttpOnly, SameSite Lax, Secure on Cloud Run, for 30 days. [src: brew/auth.js:54-64, 66, 245]
+- Every token carries its type, so a connect state never works as a session. [src: brew/auth.js:56-64]
@@ -39,2 +40,2 @@ How we keep brand data safe today, sign in, sessions, secrets, least access, the
-- Redirects after sign in go only to our own paths. [src: brew/auth.js:72]
-- Local test sign in exists only off Cloud Run. [src: brew/auth.js:249]
+- Redirects after sign in go only to our own paths. [src: brew/auth.js:73]
+- Local test sign in exists only off Cloud Run. [src: brew/auth.js:250]
@@ -47 +48 @@ How we keep brand data safe today, sign in, sessions, secrets, least access, the
-- Without a session secret on Cloud Run, sign in turns itself off instead of using a default. [src: brew/auth.js:45-46]
+- Without a session secret on Cloud Run, sign in turns itself off instead of using a default. [src: brew/auth.js:46-47]
@@ -67 +68,3 @@ How we keep brand data safe today, sign in, sessions, secrets, least access, the
-- Alerts email param@ and hello@: server errors, slow requests, worker lease lost, release failed, disk, connections, CPU, export failed, site down. [src: brew/docs/production.md:66-69]
+- Alerts email param@ and hello@: server errors, slow requests, worker lease lost, release failed, disk, connections, CPU, export failed, site down. [src: brew/docs/production.md:112-115]
+- Since 26 Oct GitHub Actions runs the whole test suite on every push and pull request, with every AI call mocked, so no key is needed in CI. [src: brew/.github/workflows/test.yml:1-16]
+- A data request deletion is a dry run first, refuses partners and protected brands, never revokes a platform grant and leaves one audit row. [src: brew/db/brand-wipe.js:7-9, 24-32, 187-195]
@@ -81 +84 @@ How we keep brand data safe today, sign in, sessions, secrets, least access, the
-- `STAFF_REQUIRE_HD` is not set on production. [src: brew/auth.js:239-241] [src: brew/tools/prod-env.sh:9]
+- `STAFF_REQUIRE_HD` is not set on production. [src: brew/auth.js:240-242] [src: brew/tools/prod-env.sh:9]
diff --git a/content/security-privacy-and-legal/subprocessors.md b/content/security-privacy-and-legal/subprocessors.md
index 45efb7c..2eb7b13 100644
--- a/content/security-privacy-and-legal/subprocessors.md
+++ b/content/security-privacy-and-legal/subprocessors.md
@@ -8 +8 @@ branded: false
-last_verified: 2026-10-26
+last_verified: "2026-10-26"
@@ -10 +10 @@ sources:
-  - "brew/docs/production.md:9-15, 66-69"
+  - "brew/docs/production.md:9-15, 112-115"