---
id: "5.9"
title: "Briefs and hand-off"
dept: engineering
status: draft
owner: Param
branded: false
last_verified: 2026-10-26
sources:
  - "brew origin/main: jam-brief.js:1-8; team-brief.js:1-20, 196-223; handoff.js:1-20; brief-status.js:1-10; jam-mail.js:1-8, 104, 117; jam2/ready.js:10-11; publish-pack.js:1-5"
  - "brew origin/main: SCOPE.md (Share the brief, Brew's emails); DUMMY.md (closing paragraph)"
  - "brew origin/main: docs/handover/engineering/12-ui2-service-api.md section 1, 13-jam-chip-api.md"
  - "brew origin/main git log: 9fd1de4 (per-tab briefs with recipients)"
  - "memory brew-brief-mode, brew-team-brief-ai-edit, brew-jam-rca-2026-10-26, brew-real-connectors"
---

# Briefs and hand-off

## Answer

Every jam ends in a brief someone else works from: the jam brief (the working document), the team brief (one per deliverable, written for a named recipient), a signed read-only page, a PDF and a publish pack; Brew sends the brief only to our team by email and never posts or runs anything itself.

## Today

### Three documents

| Document | What it is | Where it lives | Source |
|---|---|---|---|
| Jam brief | One Markdown document per jam (`w.doc`): starts as the card, grows as people jam, AI edits on request, replies fold in; readiness is a checklist, never a score | the jam | [src: brew/jam-brief.js:1-6] |
| Team brief | One AI pass turns the card, proof, plan, jam brief and final package into a brief with fixed editable sections and evidence per claim; final words verbatim; creatives attached | `w.teamBriefs[changeId]`, one per deliverable tab (`?ch=`) | [src: brew/team-brief.js:1-7, 196-200] [src: git 9fd1de4] |
| Hand-off | Everything about a jam for someone outside: card, options, Plays, brief, versions, assets, chats, open questions | `/h/<token>` read-only page, signed | [src: brew/handoff.js:1-10] |

`TB_V` 4: bump it when the team brief starts being written differently; every saved one is rewritten. [src: brew/team-brief.js:17]

### Recipients

The "Send to…" sheet names a real recipient: your team, your agency, a designer, a media buyer, a creator, a video editor, or our team. [src: brew/team-brief.js:20] [src: memory brew-jam-rca-2026-10-26]

### Send routes

| Route | What happens | Source |
|---|---|---|
| Share with my team | Freezes the brief (BR-n, numbered per brand), copies it with the read-only link; nothing sent | [src: brew/SCOPE.md] [src: brew/docs/handover/engineering/11-known-issues-and-debt.md] |
| Send to our team | Freezes and emails the whole brief to `SHOPOS_EMAIL` (default hello@joinsquareup.com) every time; tracked status | [src: brew/jam-mail.js:104] [src: brew/brief-status.js:1-6] |
| Download PDF | Chromium render, `gen/brief-*.pdf` | [src: memory brew-brief-mode] |
| Open in Gmail, Mail or Outlook | Downloads the files and opens a compose page or an .eml draft from the person's own email; no Gmail scope | [src: memory brew-brief-mode] |
| Slack | Posts to the channel picked at connect with an Approve button; thread replies fold into the brief | [src: memory brew-real-connectors] |
| Publish pack | A zip per version for the team to post or apply by hand | [src: brew/publish-pack.js:1-5] |

### Status of a brief sent to our team

`sent → received → in_progress → delivered`, or `needs_info` (needs a note) and back. Only staff move it (`POST /api/work/:wid/brief/status`). Each move writes a jam line, a notification and an email to the sender; the top bar shows "N with the team". If the email did not go, the jam says "ready, paste it", never "sent". [src: brew/brief-status.js:1-10] [src: brew/docs/handover/engineering/12-ui2-service-api.md]

### Editing a brief

The team brief writes itself once the first draft is in. Selecting text or tapping a section opens "Change with AI"; there are no hand-edit boxes. Chat edits the brief only when the message is about the brief. Every change stamps a revision so teammates reload it. [src: memory brew-team-brief-ai-edit] In the Brief tab, people can comment on words and press "Fix with AI". [src: memory brew-brief-mode]

### Link signing

Hand-off and ready links are HMAC-signed with `HANDOFF_SECRET` (production uses its own and opens nothing signed with the Anthropic key). The signature is 12 base64url characters. Links do not expire. [src: brew/handoff.js:14-20] [src: brew/jam2/ready.js:10-11]

### Guests

Guests cannot share, send or move a brief. The demo site sends no email. [src: brew/SCOPE.md]

## Tomorrow

| Item | Version | Tag |
|---|---|---|
| One team brief per deliverable with a named recipient | v1 | Stays |
| Brew never sends to a brand's customers | v1 | Stays |
| Links that expire and can be revoked | v2 | New |
| Sending a brief to an agency by email from Brew | v2 | New, needs a send decision |
| Posting the final asset to the channel | v3 | New, only behind a person's yes |

## How we do it

1. A new brief kind gets its own sections in `team-brief.js` and a test in `test/team-brief.test.js`.
2. Never let the fact sheet or team-only card words into a shared brief; `unleak()` and `publicView` guard this. [src: memory brew-brief-mode] [src: memory brew-shopify-connector-qa]
3. Check a status change end to end on staging with a staff account before relying on it.

## Numbers

| Metric | Definition | Where measured |
|---|---|---|
| Briefs shared | Frozen briefs per brand per week | `w.jamBrief` (Proposed to count) |
| With our team | Briefs sent and not yet delivered | `withTeam.n` on `/feed` and `/work` [src: brew/docs/handover/engineering/12-ui2-service-api.md] |
| Time to delivered | Sent to delivered, per brief | `w.jamBrief.history` (Proposed) |

## Open

1. **Should shared links expire?** Proposal: 30 days by default, with revoke on the jam.
2. **Does the recipient "our team" stay now that Brew is kept independent?** Proposal: keep it as is, add nothing new tied to it. [src: memory brew-qa-round2-2026-10-24]
