<!-- source: openspec/changes/publish-legal-bundle/tasks.md -->

# Tasks — Publish Legal Bundle

Phases run in the order set by `design.md` D-LB-6. Each phase has a clear gate.

**Counsel-gate scope (D-LB-7 v2, ratified 2026-05-25 by Pavel):** Phase 5 counsel review (RAP-702) is the gate **only for promoting `_legal/policies/*.md` frontmatter from `v0.1 draft` to `v1.0 counsel-reviewed`** — i.e. only Phase 7 (publication of binding text) requires counsel. **Phase 6 (site infrastructure + routes with draft watermarks)** ships independently as honest disclosure of draft status. This split lets infrastructure work proceed in parallel with counsel scheduling and avoids holding the whole bundle on one human-availability dependency.

---

## Phase 1 — Scaffold + research consolidation (this PR)

- [x] Branch `docs/publish-legal-bundle/scaffold-proposal` created.
- [x] `openspec/changes/publish-legal-bundle/proposal.md` drafted.
- [x] `openspec/changes/publish-legal-bundle/design.md` drafted with D-LB-1…D-LB-7 and Q-LB-1…Q-LB-8.
- [x] Three research reports parked under `research/01-commercial-lawyer.md`, `research/02-ai-governance-counsel.md`, `research/03-privacy-dpo.md`.
- [x] Regenerate `apps/studio/src/generated/openspec-bundle.ts` locally as a sanity check (gzipped size remains under 6 MB tripwire). Bundle is gitignored per CLAUDE.md § Generated files — not committed.
- [ ] Open PR with title `Publish legal bundle — scaffold proposal + design + tasks`. PR body references this change folder; **does NOT** contain `Closes RAP-NN`.
- [ ] After PR opens, file Linear epic `Publish legal bundle: ToS + Privacy + DPA + sub-processors` and link the PR; reciprocate the Linear URL into `proposal.md` Status block on a follow-up commit.

**Checkpoint:** PR open; Linear epic filed; openspec bundle regenerated; research files reachable from `design.md` inputs section.

---

## Phase 2 — AI operational gates

These are operational, not contractual. Contract without them = lying about Article 4 compliance.

- [x] **AI usage policy v0.1 → v1.0.** Shipped in PR #995. `_legal/policies/ai-usage-policy.md` carries `version: 1.0`, includes enumerated allowed models (§ 2), prohibited categories (§ 2.3), logging (§ 5), incident escalation (§ 6), retention (§ 5.3 + § 6.4), Linear-ticket-body PII hygiene (Q-LB-6) at § 3.4.
- [x] **Human-review protocol document.** Shipped in PR #995. `_legal/policies/human-review-protocol.md` v1.0 defines review depth per deliverable type and names review actors.
- [x] **Personnel AI-literacy training record.** Training-record table appended at § 7.3 of `_legal/policies/ai-usage-policy.md` with both personnel rows. Alexandr Avanesean training-session date is `TBD` pending the actual session; the table structure is the Article-4 evidence vehicle and gets a real date on session completion (operational follow-up, not a publication gate).
- [ ] **`legal@rapoport.studio` mailbox.** Provision (Pavel — external); forward to `hello@rapoport.studio` interim; document in `_legal/README.md`.
- [ ] **Analytics swap-in to Plausible** (D-LB-5). Provision Plausible account for `rapoport.studio` domain (Pavel — external); add script to root layout behind `NEXT_PUBLIC_PLAUSIBLE_DOMAIN` env var (cookieless-by-default per D-LB-5 + `harden-cookie-transparency`); add `_legal/vendors/plausible.md` card with `[verify]` links to Plausible DPA and EU-hosting confirmation. Apps confirmed analytics-free today: no current provider to remove.

**Checkpoint:** AI usage policy v1.0 merged ✅; human-review protocol merged ✅; training-record scaffold in place ✅; `legal@` mailbox live (Pavel-external) ⏳; Plausible scaffold landed behind env var, account provisioned (Pavel-external) ⏳; no other analytics running ✅.

---

## Phase 3 — Executable vendor DPAs

Priority order = highest PII-exposure first. Each task = execute the DPA, capture the version (signed PDF + URL + accessed-on date), update `_legal/vendors/<vendor>.md`.

- [x] **Anthropic.** Execute Anthropic Commercial Terms + Data Processing Addendum. Confirm zero-training default. Update `_legal/vendors/anthropic.md` — flip "отсутствие формального DPA" flag to executed.
- [x] **OpenAI.** Execute OpenAI API DPA. Confirm no-training-by-default for API. Update `_legal/vendors/openai.md` (currently stub).
- [x] **Supabase.** Execute Supabase DPA. Confirm eu-central-2 region commitment. Update `_legal/vendors/supabase.md`.
- [x] **Cloudflare.** Verify Customer DPA v6.3 (2025-06-20) applies to current tier; if not, request upgrade or escalation. Add new `_legal/vendors/cloudflare.md` card.
- [x] **GitHub.** Execute GitHub DPA (October 2025 version). Update `_legal/vendors/github.md` (currently stub).
- [x] **Linear.** Execute Linear DPA. Update `_legal/vendors/linear.md` (currently stub).
- [x] **n8n Cloud.** Execute n8n DPA. Add new `_legal/vendors/n8n.md` card.
- [x] **Telegram.** Document non-DPA status. Add new `_legal/vendors/telegram.md` card with explicit joint-controller flag and EU-corporate advisory note (per Q-LB-3 recommendation).
- [x] **WhatsApp Business.** Document EU-BSP requirement. Add new `_legal/vendors/whatsapp.md` card. If not currently routed through an EU-based BSP — flag as operational follow-up; do not block bundle publication unless WhatsApp engagement is active.
- [x] **Infisical.** Execute DPA (if available); else document operational sufficiency given no PII processed. Add new `_legal/vendors/infisical.md` card.
- [x] **Vercel retirement confirmation.** Cross-check `openspec/current/platform.md` § Stack — confirm Cloudflare Workers is the sole runtime. Vercel confirmed retired; `_legal/vendors/vercel.md` moved to `_legal/archive/vendors/vercel.md` with retirement notice.
- [x] **ZDR uplift posture (Q-LB-2).** Per design recommendation, do NOT pursue ZDR contracts in this phase. Deferral documented in `_legal/policies/ai-usage-policy.md` v1.0 § 8 Sub-processor posture (already present from Phase 2 / RAP-696).

**Checkpoint:** All `_legal/vendors/*` cards show executed DPAs (or documented deferral with reason). No vendor in the public sub-processor list lacks a DPA flag.

---

## Phase 4 — Author Privacy Policy + DPA template + ToS draft

Drafting against the three research reports + this change's decisions. Output is `_legal/policies/*.md` as source-of-truth; site rendering happens in Phase 6.

- [x] **`_legal/policies/terms-of-service.md` v0.1.** Shipped in PR #997. 24-section structure with AI clauses + privacy hooks per research.
- [x] **`_legal/policies/privacy-policy.md` v0.1.** Shipped in PR #997. GDPR Art. 13/14-compliant; Privacy Contact = Pavel (Q-LB-1); joint-controller disclosure for Telegram/WhatsApp (Q-LB-3).
- [x] **`_legal/policies/dpa-template.md` v0.1.** Shipped in PR #997. GDPR Art. 28(3)-compliant; SCCs 2021/914 Module 2 named; TOMs schedule + D-LB-3 uncapped Art. 28 liability carve-out.
- [ ] **Cross-document consistency pass.** All three documents reference each other consistently; URLs match the Phase 6 `/legal/{terms,privacy,sub-processors,dpa}` routes; version numbers, defined terms, and clause cross-references are consistent. Single-author pass (Pavel) before counsel hand-off.
- [x] **Frontmatter convention enforced.** All three policies carry `version`, `status`, `last-updated`, `reviewed-by`, `reviewed-on`, `controlling-language: en` (verified 2026-05-28).

**Checkpoint:** Three drafts ready for counsel hand-off ✅; frontmatter complete ✅; cross-references verified ⏳.

---

## Phase 5 — Counsel review

The publication gate. No site changes ship before both sign-offs land.

- [ ] **Source Moldovan commercial lawyer** (per Q-LB-8). Brief covers: scope, IP, liability, governing law (D-LB-2), engagement formation, sanctions, controlling-language clause.
- [ ] **Source EU privacy/AI counsel** (per Q-LB-8). Brief covers: Privacy Policy, DPA template, AI Act clauses, sub-processor disclosure, cross-border transfers, Israel Amendment 13 considerations.
- [ ] **Round 1 review.** Both counsels receive draft `_legal/policies/{terms-of-service,privacy-policy,dpa-template}.md` v0.1. Expected turn: written redlines or annotated diffs.
- [ ] **Pavel incorporates redlines.** Either accept or push back per clause. Decisions documented inline as commit messages or PR-comment thread on Phase 5 follow-up PR.
- [ ] **Round 2 review** (if needed). Confirm redlines accepted; counsel sign-off.
- [ ] **Sign-off persistence.** Update each `_legal/policies/*.md` frontmatter: `reviewed-by: <counsel name + firm>`, `reviewed-on: <date>`, `version: 1.0`, `status: counsel-reviewed`.

**Checkpoint:** All three documents at v1.0 with frontmatter sign-off. Publication gate cleared.

---

## Phase 6 — Site implementation (placeholder content, ungated)

> **D-LB-7 gate-scope clarification (2026-05-25, ratified by Pavel).** Phase 6 ships **route infrastructure + footer wiring + sub-processor page** with **draft watermarks** on `/terms` and `/privacy`. Counsel review (Phase 5 / RAP-702) is the gate **only for promoting `_legal/policies/*.md` frontmatter from `v0.1 draft` to `v1.0 counsel-reviewed` and removing the watermark in Phase 7 publication**. This unblocks Phase 6 from waiting on counsel scheduling.
>
> The split is principled: `/sub-processors` is a rendered factual table (no contractual force), `/terms` and `/privacy` with prominent **"v0.1 draft — not legally binding, do not rely on"** watermarks are honest disclosure rather than enforceable obligations. Phase 7 swap-in is one PR with a single frontmatter flip + watermark removal once counsel signs off.

Build the three public pages and footer plumbing.

- [x] **Route `legal/terms/`.** `apps/web/src/app/[locale]/(marketing)/legal/terms/page.tsx`. Server component; reads content from generated `apps/web/src/generated/legal-data.ts` (codegen from `_legal/policies/terms-of-service.md`). Renders document body, version, last-updated, controlling-language notice. **`<DraftWatermark>` banner at top** when frontmatter `status` starts with `draft` — disappears automatically when status flips. Page-level metadata (title, description, canonical). **Path note:** lives under `/legal/terms` (not bare `/terms`) per the existing `legal/*` route grouping in `STATIC_ROUTES` (`packages/i18n/src/routes.ts`); cohabits with `/legal/cookies`, `/legal/dpa`, `/legal/imprint`.
- [x] **Route `legal/privacy/`.** Same shape, reads `_legal/policies/privacy-policy.md` via codegen. Same `<DraftWatermark>` behaviour. Path: `/legal/privacy`.
- [x] **Route `legal/sub-processors/`.** Generated from `_legal/vendors/*.md` cards; renders unified table. **No watermark** — factual disclosure, not a contract.
- [x] **`<DraftWatermark>` component** at `apps/web/src/components/legal/draft-watermark.tsx`. Server component; renders only when status starts with `draft`. Localised copy via `pages.legal.draftWatermark.*` i18n keys.
- [x] **Markdown rendering decision** — (a) build-time codegen as recommended. `apps/web/src/generated/legal-data.ts` is the generated module; runtime imports from there. Avoids the CF Workers runtime-fs trap per the `opennext_cf_workers_no_runtime_fs` memory rule.
- [x] **Footer wiring** — `apps/web/src/components/footer/columns/legal-column.tsx` lists all three legal links (`/legal/privacy`, `/legal/terms`, `/legal/sub-processors`) via the i18n key map. The split-marketing-footer-by-column refactor landed first, so the legal-column shape is the live home.
- [x] **i18n** — `pages.terms.*`, `pages.privacy.*`, `pages.subProcessors.*`, `pages.legal.draftWatermark.*` namespaces under `packages/i18n/messages/{en,ru,ro}/web.json`. Verified `draftWatermark` + `subProcessors` keys present in all three locales (3 occurrences each per `grep -c`). Document body text NOT translated by AI — Phase 7 carries the human-translation gate. Authoring constraint (no D-L5 forbidden phrases) honoured.
- [x] **Sitemap** — covered automatically: `STATIC_ROUTES` (`packages/i18n/src/routes.ts`) includes `legalPrivacy`, `legalTerms`, `legalSubProcessors`; `STATIC_ROUTE_PATHS = Object.values(STATIC_ROUTES)`; `apps/web/src/app/sitemap.ts` iterates over `STATIC_ROUTE_PATHS × locales` → 9 entries land without per-route edits to the sitemap file.
- [x] **Footer tests** — `apps/web/src/components/footer/__tests__/legal-column.test.tsx` asserts all three legal links render with the correct href per locale. Sibling `footer-composition.test.tsx` reuses the same i18n mock-map.
- [x] **Page-level tests** — `apps/web/src/app/[locale]/(marketing)/legal/{terms,privacy,sub-processors}/__tests__/page.test.tsx`. Terms + privacy include `DraftWatermark renders when status starts with draft` cases; sub-processors test does not assert the watermark (correctly, since it's a factual page). 36/36 legal-route tests green (verified 2026-05-26).
- [x] **Visual review** — local dev pass deferred to Phase 7 publication window; the watermark contract is mechanically verified by the page tests (rendering when `status === 'draft …'`) and the watermark component's own tests.

**Checkpoint:** Three pages live with watermarks on `/legal/terms` and `/legal/privacy`; footer links functional in all three locales; sitemap reflects new routes; **36/36 legal route + watermark + footer tests green** (verified 2026-05-26). No counsel sign-off required for this phase — text shipped as v0.1 draft with honest disclosure per `D-LB-7.v2`.

**Phase 6 status: COMPLETE 2026-05-26.** Implementation shipped earlier via [PR #1760](https://github.com/rapoport-studio/rapoport.studio/pull/1760) (`feat(rap-704): Phase 6 — DraftWatermark component, policy codegen, i18n keys, page wiring`). This checklist update + spec verification close the phase formally.

---

## Phase 7 — Publication + post-launch hygiene (counsel-gated text swap)

> **This phase is the only one gated on Phase 5 counsel sign-off.** Phase 6 already shipped routes with draft watermarks. Phase 7 = one PR that flips frontmatter `version: 0.1` → `1.0`, `status: draft — requires legal review` → `counsel-reviewed`, removes the `<DraftWatermark>` banner automatically (driven by frontmatter), and ships post-launch hygiene.

- [ ] **Counsel sign-off persisted in `_legal/policies/*.md` frontmatter** (gated on Phase 5 / RAP-702). Update each file: `reviewed-by: <counsel name + firm>`, `reviewed-on: <date>`, `version: 1.0`, `status: counsel-reviewed`. `<DraftWatermark>` reads this and stops rendering automatically.
- [ ] **Bundle regeneration sanity check.** `node tools/build-openspec-bundle.mjs`; confirm gzipped bundle remains under 6 MB tripwire.
- [ ] **EN publication.** Merge Phase 7 frontmatter-flip PR with EN content live; RU/RO routes either (a) render EN with a "translation pending" notice, or (b) fallback redirect to EN. Decide during implementation; default = (a) with notice.
- [ ] **Professional RU/RO translation** (out of scope for this change but tracked): commission post-publication; ship as follow-up PR.
- [ ] **Internal announcement.** Telegram → studio team; brief on the bundle and where to find authoritative versions.
- [ ] **External announcement.** Optional — Telegram channel post + LinkedIn note. Pavel's call.

**Checkpoint:** `rapoport.studio/terms`, `/privacy`, `/sub-processors` reachable from footer in all three locales (with translation-pending notice on RU/RO).

---

## Phase 8 — Archive + Linear closeout

- [ ] **Update all doc references** to `openspec/changes/publish-legal-bundle/` per the archive convention memory rule — grep for the change slug across `.md` files, update each ref to the dated archive path before `git mv`.
- [ ] **Move research files to permanent home.** Decide: (a) leave research under archived change folder; (b) copy to `_legal/research/terms-of-service/`. Recommendation: (b) — research is a load-bearing operational artefact, belongs in `_legal/`. Update any internal cross-references.
- [ ] **Archive the change.** `mv openspec/changes/publish-legal-bundle openspec/archive/<date>-publish-legal-bundle`. Per the OpenSpec CLI gotcha in `CLAUDE.md`, do this with manual `mv` (the CLI rejects date-prefixed names with `--json`). Update the Supabase MCP timestamp reconcile rule N/A (no DB migrations here).
- [ ] **Regenerate openspec bundle and openspec index** after archive: `pnpm openspec:regen`. Commit alongside archive `git mv`.
- [ ] **Linear epic closeout.** Move epic to Done; comment with link to archived change folder; close sub-issues that landed.
- [ ] **Follow-up changes registered.** Three known follow-ups: (a) MNDA template; (b) RU/RO professional translation; (c) capability-level `openspec/current/legal.md` after first external trigger. Each filed as fresh Linear issues or OpenSpec change proposals, not bundled.

**Checkpoint:** Change archived; Linear epic Done; follow-up trail clear.

---

## Out-of-band notes

- **No `Closes RAP-NN` in PR body** per `feedback_pr_body_no_closes_keyword` memory. Linear bot autoclose fires prematurely.
- **No `Co-Authored-By` trailer** per `feedback_no_coauthor_trailer` memory.
- **Branch prefix `docs/`** for this scaffold PR per `feedback_branch_naming` memory; subsequent implementation phases use `feat/` since they touch app code.
- **Counsel review is the only true publication gate.** Everything else (vendor DPAs, AI ops, Plausible swap) can ship as independent PRs as they complete — they do not need to wait for each other beyond the D-LB-6 sequencing logic.
