Skip to content
This repository was archived by the owner on Jul 27, 2026. It is now read-only.

Add Privacy Policy and Terms of Service scaffolds - #1

Open
grantfowler wants to merge 1 commit into
mainfrom
legal-pages
Open

Add Privacy Policy and Terms of Service scaffolds#1
grantfowler wants to merge 1 commit into
mainfrom
legal-pages

Conversation

@grantfowler

Copy link
Copy Markdown
Member

Why

App Store Connect and the Play Data Safety form both require a reachable policy URL before a build can be submitted, and Stripe expects terms at checkout. Neither document existed anywhere. This is one of four launch blockers being cleared; the other three are in samplify-app#launch-blockers.

They live here rather than in the app because the URL is a compliance artifact third parties open on their schedule — it must not depend on a Vercel deploy being healthy, and legal text changes on a different cadence than the product.

What this is — and is not

The routes, layout, links, and publish machinery are complete. The section bodies are deliberately empty. Each section carries a note describing what it must cover, derived from the data flows that actually exist in the product — label photos going to Anthropic for OCR, numbers to Twilio, billing to Stripe, the 60-day pre-checkout trial, the consent obligation that sits with the store. Counsel writes the prose.

Privacy has 11 sections, Terms has 14.

Publishing is all-or-nothing, enforced at build time

The first version of this had a real hole, caught in review: published gated only the draft banner and noindex, while the body/note fallback keyed off body independently. Writing 9 of 11 sections and flipping the flag shipped an indexable, banner-free "Privacy Policy" whose remaining sections rendered their internal counsel notes as operative policy text.

Now the build throws if a document is published with any unwritten section, or without an effective date. Verified three ways:

  • half-published → build fails, naming every unwritten section
  • published with a null effective date → build fails
  • fully published → builds clean, enters the sitemap, no noindex, no banner, no note leakage

src/legal-docs.mjs is the single source of truth for publish state, imported by both the pages and astro.config.mjs, so the sitemap exclusion derives from the same flag rather than a second hand-maintained list. Previously those drifted silently in both directions — a live policy missing from the sitemap, or a noindexed draft advertised in it.

To publish

  1. Write every section body in src/pages/privacy.astro / terms.astro
  2. Set published: true and an effective date in src/legal-docs.mjs

That's it — the sitemap follows automatically, and a half-finished attempt fails the deploy.

Merging publishes the drafts

This repo auto-deploys to GitHub Pages on push to main, so merging makes /privacy and /terms publicly reachable immediately — as noindex pages carrying a visible "Draft — not a published policy" banner, excluded from the sitemap. That is the designed behavior and it is safe, but it is a real decision: you may prefer to hold this merge until counsel supplies the copy.

Also fixed from review

  • .prose-legal gave h3 weight but not size — Tailwind preflight makes headings inherit, so subheads in injected policy HTML would have rendered flat
  • No table styles, despite counsel being asked to tabulate the sub-processor list (the shape Play's Data Safety reviewers expect)
  • > * + * is direct-children-only, so paragraphs inside a list item collapsed together
  • role="alert" on load-time static content → role="note" with aria-labelledby

Verification

npm run build clean. Checked in generated output: both pages emit noindex, sitemap contains only /, canonicals correct, every ToC anchor matches a section id, footer links resolve.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FxVqvypw6Y1GmeVKr44XgR

App Store Connect and the Play Data Safety form both require a reachable
policy URL before a build can be submitted, and Stripe expects terms at
checkout. Neither existed anywhere.

These live here rather than in the app because the URL is a compliance
artifact third parties open on their own schedule -- it must not depend on
a Vercel deploy being healthy, and legal text changes on a different cadence
than the product.

The routes, layout, links, and publish machinery are complete; the section
BODIES ARE DELIBERATELY EMPTY. Each section carries a note describing what
it must cover, derived from the data flows that actually exist in the
product (label photos to Anthropic for OCR, numbers to Twilio, billing to
Stripe, the 60-day pre-checkout trial, the consent obligation that sits with
the store). Counsel writes the prose.

Publishing is all-or-nothing and enforced at build time: a document with any
unwritten section, or without an effective date, throws rather than shipping
counsel notes as operative policy text. src/legal-docs.mjs is the single
source of truth for publish state, so the sitemap exclusion derives from the
same flag instead of a second hand-maintained list that could drift.

Until published, both pages render a draft banner, emit noindex, and stay
out of the sitemap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FxVqvypw6Y1GmeVKr44XgR
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant