{
 "id": "5.4",
 "title": "Environments",
 "department": "Engineering",
 "department_slug": "engineering",
 "direction": null,
 "branded": false,
 "status": "draft",
 "owner": "Param",
 "last_verified": "Mon Oct 26",
 "holds": "Production, staging, demo, local; who uses each; what moves between them",
 "urls": {
  "html": "https://handbook.joinsquareup.com/engineering/environments",
  "markdown": "https://handbook.joinsquareup.com/engineering/environments.md",
  "json": "https://handbook.joinsquareup.com/engineering/environments.json"
 },
 "other_directions": [],
 "tldr": {
  "headline": "Four places Brew runs, and each brand has exactly one home",
  "points": [
   {
    "icon": "map",
    "text": "Production, staging, the demo and a laptop, each with its own release path."
   },
   {
    "icon": "people",
    "text": "All five partners call production home since 25 Oct."
   },
   {
    "icon": "lock",
    "text": "Demo mode, AI mock and open sign in never run on production."
   },
   {
    "icon": "warn",
    "text": "Two code comments still say Arata's home is staging."
   },
   {
    "icon": "rocket",
    "text": "Production is released only by promote.sh, behind the clean gate."
   }
  ],
  "stat": {
   "value": "5",
   "label": "partners homed on production"
  },
  "decide": "Is Arata's home production or staging? Proposal: production, and fix the two comments.",
  "mood": "solid"
 },
 "sources": [
  "brew/.wt-prod/docs/production.md",
  "brew origin/main: deploy.sh:20-55, tools/prod-env.sh, tools/deploy-demo.sh, tools/promote.sh, moved.js:1-15, server.js:1119-1124, db/prod-clean.mjs:1-30",
  "brew origin/main: README.md 'Run it locally', DUMMY.md rows 1, 5, 6, 8",
  "brew origin/main git log: deb7d90, 119dc8a, 41b0258 (25 Oct, which site is home)",
  "memory brew-prod-backend, prod-clean-only, brew-live-stores, brew-demo-mode, always-deploy-to-test, brew-setup-v2"
 ],
 "open_questions": [
  "**Is Arata's home production or staging?** The settings say production; two comments say staging. Proposal: production, and fix the two comments.",
  "**When does production get its own domain?** Proposal: keep feed.joinsquareup.com until the brand name is picked, then move with a redirect.",
  "**Should Sloosh finals run on production?** Proposal: no until the Sloosh workspace has paid credits and Param approves the first quote there."
 ],
 "parts": {
  "Answer": "Brew runs in four places, production, staging, the demo and a laptop, and this page settles who uses each, what may run there and how code and brands move between them.",
  "Today": "| | Production | Staging | Demo | Local |\n|---|---|---|---|---|\n| Address | feed.joinsquareup.com | brew.joinsquareup.com | demo.joinsquareup.com | http://localhost:4200 |\n| Cloud Run | `brew-web` + `brew-worker` | `shopos-feed` | `brew-demo` | none |\n| Storage | Cloud SQL `brew-prod` + `gs://brew-prod` | Cloud SQL `brew-staging` + `gs://brew-prototype` (mirrored) | `gs://brew-prototype-demo` only | `.brands/`, `.tenants/` folders |\n| Who uses it | The five design partners and our team | Our team, testing | Prospects, a sandbox per visitor | One developer |\n| AI | Gemini: Pro for Plays, Flash for the rest | same | all Flash | `AI_MOCK=1` |\n| Released by | `tools/promote.sh` only | `./deploy.sh` | `tools/deploy-demo.sh` | `node server.js` |\n| Admins | `param@joinsquareup.com` only, plus full-access domains joinsquareup.com and shopos.ai | three admin emails | same as staging | `DEV_MEMBER=1` |\n\nSources: [src: brew/.wt-prod/docs/production.md] [src: brew/deploy.sh:39] [src: brew/tools/prod-env.sh:9] [src: brew/tools/deploy-demo.sh] [src: brew/README.md]\n\n### Which site is a brand's home\n\n- Since 25 Oct (evening) every design partner's home is production: Arata, Nutrova, Mana, PUMA India and Scrubroom. [src: git 119dc8a] [src: memory brew-prod-backend]\n- Staging lists them in `MOVED_TO_PROD`: their own people get a 302 (pages) or 409 (API) to production; staff stay. [src: brew/deploy.sh:39] [src: brew/server.js:1119-1124]\n- Production's `STAGING_HOME` does the reverse and is empty. [src: brew/tools/prod-env.sh:9]\n- A brand at home on one site gets no background work, outgoing email or Slack, or sign-in renewal from the other. Connector reads keep running on both, so Arata stays connected on both. [src: brew/moved.js:1-8] [src: brew/.wt-prod/docs/production.md]\n- Two code comments still say Arata's home is staging again (deploy.sh's smoke list and moved.js's header). They date from commit deb7d90, which 119dc8a reversed 30 minutes later. [src: brew/deploy.sh:51] [src: brew/moved.js:5-6] [src: git deb7d90, 119dc8a]\n- Cozy Snugglet and Zenant are our own test brands. Other slugs on staging are leftovers. [src: memory brew-live-stores]\n\n### What may run where\n\n| Rule | Production | Staging | Demo | Source |\n|---|---|---|---|---|\n| `DEMO_MODE`, `AI_MOCK`, `/auth/dev`, `OPEN_SITE`, simulated connectors | never (clean gate refuses) | never on purpose | `DEMO_MODE=1` | [src: brew/db/prod-clean.mjs:1-6] [src: memory prod-clean-only] |\n| Test brands and test keys | never | yes | prepared brands only | [src: brew/db/prod-clean.mjs:12] |\n| Sloosh finals | off (Gemini fallback) | connected | simulated, Dummy | [src: git 0fc7cfc] [src: brew/DUMMY.md] |\n| Simulated teammate replies | no | no | yes, tagged | [src: brew/DUMMY.md] |\n| Outgoing email | yes | staff and non-moved brands | blocked | [src: brew/DUMMY.md] [src: brew/moved.js:1-6] |\n| Kept exceptions in production | Pro plan sheet (no billing), Marketplaces \"Coming soon\", catalog-photo fallback, Delete my data as an email request | | | [src: memory prod-clean-only] |\n\n### What moves between them\n\n| From → to | What moves | How | Source |\n|---|---|---|---|\n| main → staging | code | `./deploy.sh` from a clean tree of origin/main | [src: brew/deploy.sh:7] |\n| staging → production | the exact image staging runs, by digest | `tools/promote.sh` | [src: brew/tools/promote.sh:1-20] |\n| staging → production | a partner's data, once | `tools/partner-copy.sh` (job `brew-partner-copy`): rows, parts, vault re-encrypted, binaries; never jams, finals, `raw/`, spend or test data | [src: memory brew-prod-backend] |\n| staging → demo | a prepared brand's public pack | `tools/demo-sync.sh <slug>` then `tools/demo-strip.mjs` and a canary | [src: memory brew-meta-connector-qa] |\n| production → anywhere | nothing | | |\n\nWork kept on staging on purpose: the Set-up v2 composer was not promoted as of 25 Oct by Param's choice. [src: memory brew-setup-v2]\n\n### Local\n\n```bash\nnpm ci\nAI_MOCK=1 BUILD_RESUME_OFF=1 RESEARCH_OFF=1 LIVE_OFF=1 TREG_OFF=1 FEED_SIGNIN=0 node server.js\n```\n\nKeys come from `../ugc-factory/.env` (override with `ENV_FILE`). Only test brands serve locally; partners live on production. [src: brew/README.md] [src: memory brew-jam-rca-2026-10-26]",
  "Tomorrow": "| Item | Version | Tag |\n|---|---|---|\n| Production as the only home for real brands | v1 | Stays |\n| A new production domain to replace feed.joinsquareup.com | v2 | Changes (domain open) |\n| Staging bucket mirror | v2 | Goes |\n| Demo on its own Postgres | v2 | New |\n| Shopify public app pointed at production after review | v1 | Changes, once Shopify approves |\n| Sloosh connected on production | v2 | Open, Param's call |",
  "How we do it": "1. Test on staging. Deploy right after a change is tested; Param tests on the live staging URL. [src: memory always-deploy-to-test]\n2. Release to production only with `tools/promote.sh`, from a clean checkout of origin/main. See Release and promote.\n3. Run risky tests (disconnects, resets) only on Cozy or Zenant on staging. [src: memory brew-live-stores]\n4. Never wipe or reset the five partners. [src: memory brew-live-stores]",
  "Numbers": "| Metric | Definition | Where measured |\n|---|---|---|\n| Brands per site | Brands whose home is each site | `MOVED_TO_PROD` and `STAGING_HOME` |\n| Staging to production lag | Commits on origin/main not yet promoted | `git log <prod git-sha>..origin/main` (Proposed) |\n| Clean gate findings | Findings per promote run | `brew-prod-clean` job logs |",
  "Open": "1. **Is Arata's home production or staging?** The settings say production; two comments say staging. Proposal: production, and fix the two comments.\n2. **When does production get its own domain?** Proposal: keep feed.joinsquareup.com until the brand name is picked, then move with a redirect.\n3. **Should Sloosh finals run on production?** Proposal: no until the Sloosh workspace has paid credits and Param approves the first quote there."
 }
}