Skip to content

Define account setup service email as its own narrow category - #170

Open
TommyBez wants to merge 2 commits into
mainfrom
gtm/email-service-class-contract
Open

Define account setup service email as its own narrow category#170
TommyBez wants to merge 2 commits into
mainfrom
gtm/email-service-class-contract

Conversation

@TommyBez

@TommyBez TommyBez commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Hypothesis

The first two activation emails need a basis that gives them recipients. Opt-in consent to product_communications currently stands at 0 of 8 accounts, so an activation sequence built on that topic would send nothing to nobody. Classifying only the welcome message and the first skill reminder as account setup service email gives the sequence a narrow, defensible set of recipients: the person who just created a team, inside the window where setting up that account is still the thing they are doing.

What changes

Documents only. Two files.

.agents/product-marketing.md

  • "activation guidance" is removed from the list of things that share the product_communications consent topic.
  • A new bullet defines account setup service email: only the welcome message and the first skill reminder, to the team creator, within 14 days of team creation, capped at 3 proactive emails per person ever, each carrying a visible unsubscribe, with every suppression honored. Its basis is the setup of the account the user just created, not marketing consent. Later activation messages, inviting a teammate and connecting an agent, stay under product_communications and still require opt-in consent.
  • The founder-voice sender rule is harmonized. For this category, and only this category, the opt-in consent check is replaced by qualification under the category. Suppression, unsubscribe, identity, provider, a real public postal identity, and explicit send authorization stay mandatory for it too.

docs/email-compliance.md

  • A new "Activation service email" section documents the operational contract: perimeter (activation_welcome and activation_first_skill only), the 14 day window from team creation, the legal basis (legitimate interest toward a registered user limited to completing the setup of the account they just created in the EU, CAN SPAM footer with a working opt out in the United States), the frequency caps (at most one per user per day, at least 24 hours between two emails of the sequence, no more than 3 activation emails per person ever), the permanent exit conditions (withdrawn consent, any suppression, the completed action, the end of the window, or 3 emails sent), the absolute precedence of suppression, reuse of the existing one-click unsubscribe and its RFC 8058 headers, exclusion of the emailSubscriber list, no retroactive send to existing users without a separate decision, and the two preconditions on the very first send.

What does not change

  • No code. No template, no scheduler, no send path, no schema, no provider resource.
  • No send is enabled or authorized by this PR. Nothing goes out without Tommy's explicit authorization, which the doc restates as a precondition.
  • The product_communications opt-in disclosure is identical and PRODUCT_COMMUNICATIONS_NOTICE_VERSION is untouched. The invite and connect emails stay under the opt-in topic, so the disclosure users already answered stays accurate.
  • lib/site.ts still carries a placeholder postal address. Replacing it with the operator's real address is written into the doc as a precondition for the first send, not done here.

The metric that has to move

Share of newly created teams that save their first skill within 14 days of team creation.

Read it directionally over months, not weekly. At current volume a single team swings the number, so any short window reading is noise rather than signal.

Guard metric: spam complaints. One complaint stops the sequence.

How we will know it worked

The sequence does not exist yet, so this PR cannot be judged on outcome. It can be judged on whether the classification is narrow enough to survive review: two messages, one recipient, one window, one hard cap, five independent exit conditions, and no route by which a newsletter, a digest, or a re-engagement message could later claim the same basis. If a reviewer can name a message that would slip into this category and should not, the definition is wrong and this should not merge.

Once the sequence ships and sends, the check is the activation share above against its pre-sequence baseline, with complaints at zero.

Approval

The classification is approved by the founder: decision Q31a, option b restricted, over Slack. The contract itself requires that a material change to the durable communication policy be delivered through the normal repository review process, which is why this PR lands before any sending code is written.

Verification

tsc --noEmit clean. node --test tests/*.test.mjs green, 562 of 562. No test reads either document, and no test regressed.

Q31a, option b restricted, approved by the founder. The first two
activation emails need a basis that gives them recipients: opt-in
consent to product_communications is 0 of 8, so the sequence would
send nothing.

.agents/product-marketing.md drops "activation guidance" from the
product_communications topic and defines account setup service email:
welcome and first skill only, to the team creator, inside 14 days of
team creation, 3 proactive emails per person ever, visible
unsubscribe, every suppression honored. The founder-voice sender rule
now says the opt-in check is replaced by qualification under that
category for service email only, and that suppression, unsubscribe,
identity, provider, a real public postal identity, and explicit send
authorization stay mandatory for it too.

docs/email-compliance.md documents the operational contract: perimeter,
window, legal basis, frequency caps, permanent exit conditions,
suppression precedence, reuse of the existing one-click unsubscribe,
emailSubscriber exclusion, no retroactive send, and the two
preconditions on the first send.

No code changes. No send is enabled. The product_communications
disclosure and its notice version are untouched.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
skillsboard Ready Ready Preview, v0 Sep 1, 2026 7:18pm UTC

Request Review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T06:34:38.771949Z 3abdac3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 2306e078-1430-4f54-9c76-4db0d7656ac3


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3abdac3c4c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/email-compliance.md Outdated
- The `emailSubscriber` list is out of scope. Those rows are visitor captures with no account and no team, so they never receive an activation email.
- No retroactive send. Users and teams created before the sequence ships are not backfilled into it, and doing so would need its own recorded decision.

Two preconditions gate the first send of the sequence. The footer must carry a real public postal identity: the value in `lib/site.ts`, quoted above, is a placeholder and has to be replaced with the operator's real address before any activation email leaves. And the founder has to authorize that first send explicitly, as with every other proactive category.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require a privacy-notice update before the first send

When this category is enabled, the two listed preconditions are insufficient: the current public privacy notice says Skills Board sends requested transactional messages such as sign-in codes and invitations, and sends practical guides or other product communications only after opt-in (app/privacy/page.tsx lines 84-124). Allowing activation_welcome and activation_first_skill under legitimate interest without first updating that notice would begin using Resend for a purpose users have not been told about. Add privacy-notice alignment and publication as a precondition to the first activation send.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Founded, and fixed in 4d951ef.

The precondition list in docs/email-compliance.md now opens with the privacy notice: it has to describe this category and the basis it relies on before the category is switched on, because the notice published today says product communications go out only after opt-in. The explicit send authorization stays as the second precondition. Publishing the notice and enabling the send both remain manual steps.

The other precondition you quoted is gone, on a separate maintainer decision. A public postal address is not required for this category: CAN SPAM asks for a postal address on commercial messages, and account setup service email is a relationship message. The placeholder address it pointed at was invented, so it was removed from lib/site.ts in the same commit rather than replaced, and every surface omits the address while none is configured. A real postal address stays required before a marketing broadcast.

…to marketing

Q31d, approved by the maintainer. The CAN SPAM postal address requirement
covers commercial messages. Account setup service email and transactional
email are relationship messages, so they do not need one, and the address
that was in `lib/site.ts` was invented: "15 Giuseppe Verdi Avenue, Suite
150, Capraia Innovation Park, CA 50050" is not a real place. A fabricated
address in an email footer, on the terms and privacy pages, and inside the
Organization JSON-LD is worse than no address at all.

`postalAddress` and its structured twin `address` are now nullable and set
to null. Every surface that printed them omits the block while they are
unset: the legal page footer, the marketing email footer, the terms and
privacy contact paragraphs, the contact page card, and the Organization
JSON-LD, which now emits `address` only when there is a real one. Setting
the two fields together, with a real address, restores every surface at
once. No address was invented to replace the old one.

docs/email-compliance.md now states that a marketing broadcast carries the
public postal address and that service and transactional email do not, and
that the address is required before the first broadcast to the opt-in list
rather than before the first activation email. `.agents/product-marketing.md`
is aligned on the same point for the service category.

The Codex P2 on this PR is addressed in the same paragraph: the first send
of the sequence is now gated on the public privacy notice describing this
category and its basis, plus the explicit send authorization that was
already there. Publishing a notice and enabling a send both stay manual.

The organization node test asserts the address is absent while the two
spellings are unset, that neither is ever set alone, and keeps the
agreement check for when a real address exists.

tsc --noEmit clean. 562 unit tests pass. No send is enabled.
@TommyBez

TommyBez commented Sep 1, 2026

Copy link
Copy Markdown
Owner Author

Pushed 4d951ef after the maintainer approved the postal address question.

The category this PR defines is account setup service email. The CAN SPAM postal address requirement applies to commercial messages, and service and relationship email are not commercial messages, so a physical postal address is not a precondition for the first send of this sequence. A real postal address stays required before any future marketing broadcast to the opt-in list, such as a newsletter or a product update. Approved by maintainer.

What changed:

  • lib/site.ts: the postal address that lived there was invented, so it is gone. postalAddress and its structured twin address are now nullable and set to null. No new address was invented to replace it.
  • Every surface that printed it now omits the block while it is unset: the legal page footer, the marketing email footer, the terms and privacy contact paragraphs, the contact page card, and the Organization JSON-LD, which emits address only when a real one exists. Setting the two fields together, with a real address, restores all of them at once.
  • docs/email-compliance.md: the postal address is documented as a marketing broadcast requirement rather than a service email one, and the first send precondition was rewritten accordingly.
  • .agents/product-marketing.md: aligned on the same point for the service category.
  • tests/developer-surface.test.mjs: the organization node test asserts the address is absent while both spellings are unset, that neither is ever set alone, and keeps the agreement check for when a real address exists.

This also picks up the Codex P2 on this PR. Privacy notice alignment is now one of the two preconditions on the first send, next to the explicit send authorization.

Note that this PR is no longer docs only. It now touches lib/site.ts, lib/seo/organization.ts, lib/seo consumers through that helper, the legal page shell, the marketing email footer, three page files, and one test file. The visible effect: the terms, privacy, and contact pages and the legal page footer no longer show a postal address, and the Organization JSON-LD carries no address node until a real address is configured. Nothing is sent by this PR and no send is enabled.

tsc --noEmit is clean and all 562 unit tests pass locally.

PR #172 branches from main and still carries the older version of these two documents, so it needs a rebase on this branch before its diff reads consistently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant