{
 "id": "5.14",
 "title": "Parallel sessions",
 "department": "Engineering",
 "department_slug": "engineering",
 "direction": null,
 "branded": false,
 "status": "draft",
 "owner": "Param",
 "last_verified": "Mon Oct 26",
 "holds": "Worktrees, staging only your own lines, deploying from a clean tree",
 "urls": {
  "html": "https://handbook.joinsquareup.com/engineering/parallel-sessions",
  "markdown": "https://handbook.joinsquareup.com/engineering/parallel-sessions.md",
  "json": "https://handbook.joinsquareup.com/engineering/parallel-sessions.json"
 },
 "other_directions": [],
 "tldr": {
  "headline": "Sessions edit brew in parallel, so each ships only its own lines",
  "points": [
   {
    "icon": "people",
    "text": "Commit only your own lines, and push to origin/main before deploying."
   },
   {
    "icon": "box",
    "text": "Deploy from a clean detached worktree of origin/main, never a local main."
   },
   {
    "icon": "warn",
    "text": "16 and 19 Oct: unpushed or swept in code was shipped or dropped."
   },
   {
    "icon": "lock",
    "text": "Never edit a tree or script while it is deploying."
   },
   {
    "icon": "gear",
    "text": "deploy.sh refuses a dirty tree, and one promote runs at a time."
   }
  ],
  "stat": null,
  "decide": "Should sessions move to branches and pull requests? Proposal: yes for changes over 200 lines.",
  "mood": "solid"
 },
 "sources": [
  "brew origin/main: CLAUDE.md 'Release rhythm'; deploy.sh:7, 11-16; tools/promote.sh:28-38",
  "memory brew-parallel-sessions-deploy, brew-qa-sweep-2026-10-19, brew-community-seat, brew-move-freshness, brew-one-stage, brew-qa-round2-2026-10-24, brew-v1-creative-stage, brew-jam-v2, brew-arata-rewrite-2026-10-15, brew-read-stream-backfill, brew-shopify-app-review, brew-team-jams"
 ],
 "open_questions": [
  "**Should sessions move to branches and pull requests now?** Proposal: yes for any change over 200 lines; small fixes stay on main.",
  "**Should long paid runs block deploys?** Proposal: no; keep resumable runs and never re-add a deploy wait."
 ],
 "parts": {
  "Answer": "Several Claude sessions edit and deploy brew at the same time, so each one commits only its own lines, pushes to origin/main before deploying, and deploys from a clean detached worktree of origin/main; this page is the runbook that stops one session from shipping or erasing another's work.",
  "Today": "### What went wrong before\n\n| Date | What happened | Lesson | Source |\n|---|---|---|---|\n| 16 Oct | A commit of `public/app.js` swept in another session's half-done Slack and menu code | Check `git diff <file>` before adding; stage only your hunks | [src: memory brew-parallel-sessions-deploy] |\n| 16 Oct | Four local-only commits were skipped by two deploys from origin's older HEAD | Push, or your commits never ship | same |\n| 19 Oct | A hunk-filtered commit put a click listener inside the wrong function | Build the staged file from `git show HEAD:<file>`; check `git diff --cached` | same |\n| 19 Oct | A deploy from a local main without pushed fixes dropped them from live | Deploy from a fresh worktree of origin/main; check the served `app.js?v=` hash | [src: memory brew-qa-sweep-2026-10-19] [src: memory brew-community-seat] |\n| 15 Oct, 23 Oct, 24 Oct | Deploys every few minutes cut a paid rewrite, a 4-slide carousel and a read stream | Paid runs resume and save progress; before a long paid run, expect deploy churn | [src: memory brew-arata-rewrite-2026-10-15] [src: memory brew-v1-creative-stage] [src: memory brew-read-stream-backfill] |\n| 24 Oct, 25 Oct | Editing a worktree while `./deploy.sh` ran from it; editing `promote.sh` while it ran | Never edit a tree or script that is deploying | [src: memory brew-qa-round2-2026-10-24] [src: memory brew-prod-backend] |\n| | A rebase by another session dropped a hunk | After any rebase, check HEAD still has your lines | [src: memory brew-move-freshness] |\n\n### What the code does to help\n\n- `deploy.sh` refuses a dirty tree and retries the suite once, because two suites side by side can trip each other. [src: brew/deploy.sh:7, 11-16]\n- Background work survives a deploy: SIGTERM leaves runs unfinished and the next instance resumes them. [src: brew/CLAUDE.md]\n- One promote at a time through a lock in `gs://brew-prod-backups/locks/`. [src: brew/tools/promote.sh:28-38]\n- Revisions carry a `git-sha` label. [src: brew/deploy.sh:41]",
  "Tomorrow": "| Item | Version | Tag |\n|---|---|---|\n| Commit only your own lines, push, deploy from origin/main | v1 | Stays |\n| A branch per session with a pull request into main | v2 | New, once Sai and Arvind code daily |\n| CI on main so a broken push is seen before anyone deploys it | v1 | New |\n| A shared \"who is deploying\" note | v2 | New |",
  "How we do it": "1. Before adding a file, look at its diff. If another session has hunks in it, stage only yours:\n\n```bash\ngit -C brew diff <file>\ngit -C brew show HEAD:<file> > $TMPDIR/f && <apply your edit to $TMPDIR/f>\ngit -C brew update-index --cacheinfo 100644,$(git -C brew hash-object -w $TMPDIR/f),<file>\ngit -C brew diff --cached <file>\n```\n\n2. Commit, rebase and push before deploying:\n\n```bash\ngit -C brew pull --rebase && git -C brew push origin HEAD:main\n```\n\n3. Deploy from a detached worktree of origin/main, with node_modules and the brand folder linked:\n\n```bash\ngit -C brew fetch origin\ngit -C brew worktree add --detach $TMPDIR/brew-deploy origin/main\nln -s /Users/param/Documents/ChatGPT/brew/node_modules $TMPDIR/brew-deploy/node_modules\ncd $TMPDIR/brew-deploy && BRANDS_DIR=/Users/param/Documents/ChatGPT/brew/.brands ./deploy.sh\ngit -C /Users/param/Documents/ChatGPT/brew worktree remove $TMPDIR/brew-deploy\n```\n\n[src: memory brew-parallel-sessions-deploy] A fresh worktree may lack modules other branches added (for example `pg`, `sharp`); link the right node_modules. [src: memory brew-one-stage] [src: memory brew-jam-v2]\n\n4. Confirm on the live site by grepping the served `app.js` for a new identifier, and check the revision's `git-sha` descends from your commit. [src: memory brew-qa-sweep-2026-10-19]\n5. Never stash, reset or overwrite another session's work. [src: memory brew-parallel-sessions-deploy]\n6. When a live-only bug cannot be reproduced locally, diff the served file against your working copy first. [src: memory brew-parallel-sessions-deploy]",
  "Numbers": "| Metric | Definition | Where measured |\n|---|---|---|\n| Lost-commit incidents | Deploys that shipped without a pushed commit | Proposed: compare each revision's git-sha to origin/main |\n| Deploys cutting paid runs | Paid runs resumed after a restart | `[build]` and `[finals]` resume logs |",
  "Open": "1. **Should sessions move to branches and pull requests now?** Proposal: yes for any change over 200 lines; small fixes stay on main.\n2. **Should long paid runs block deploys?** Proposal: no; keep resumable runs and never re-add a deploy wait."
 }
}