Skip to content

Disclose Jam in the privacy policy and fix public copy (T14: SC-01, SE-03, SE-04, SE-05, AX-03) - #209

Merged
Programcode merged 4 commits into
mainfrom
fix/t14-public-copy-privacy
Oct 23, 2026
Merged

Programcode merged 4 commits into
mainfrom
fix/t14-public-copy-privacy

Conversation

@Programcode

@Programcode Programcode commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Batch T14 from the 22 Oct 2026 QA report: the Jam disclosure in the privacy policy, plus public-site copy, guide structure and the JSON-LD logo.

Important

Adam: please read the privacy policy diff in all three languages before merging. The Spanish and Hungarian legal text is my translation and nobody else has reviewed it. Three decisions are still yours (see "Open decisions").

Issues

SC-01 (High), privacy-policy part: disclose Jam

  • New section 8. Bug Reports with Jam in EN, ES and HU (privacyPage.s8/p8). The old sections 8 to 10 become 9 to 11 and PrivacyPage.tsx renders 11. "Last updated" moves to October 23, 2026 in all three languages.
  • The section is written for what main ships today (src/jam-loader.ts, src/lib/jam-metadata.ts, AuthProvider.tsx, index.html):
    • who runs it: We are Jam, Inc., as our service provider;
    • when it loads: for every visitor, after the first click, tap or key press, or 8 s after load;
    • what the frame from recorder.jam.dev receives: page address, browser and device details and load timings;
    • what the scripts collect: console logs, network requests, clicks and key presses, so they can be attached to a recording;
    • what a recording contains: a video of the shared screen, window or tab plus that data, made when someone opens a Jam recording link and presses Start Recording;
    • the signed-in account ID is available to Jam and is attached to recordings;
    • retention: Jam publishes no fixed period (its privacy policy says data is kept "until it is no longer needed");
    • how to ask for deletion or object: privacy@debatelab.app.
  • It does not claim auto-blur or sanitizers, and it does not claim that nothing leaves the browser before a recording starts. I couldn't verify either.
  • Data Sharing (s5) said "we share data only with OpenRouter, ElevenLabs and payment processors". Now it also names Exa (web research), PostHog and Sentry (already disclosed in s7) and Jam. The ES/HU substrings that e2e/voice-rounds.spec.ts asserts are unchanged.
  • The Terms are untouched. jam-metadata.ts, jam-loader.ts, the CSP and consent are untouched.

What I observed on prod (Playwright, signed out, 15 s per page; this is evidence for the PR only, not wording for the policy):

  • The scripts load on /, /privacy and /tools but not on the static guide pages.
  • The frame posts Cloudflare RUM timings and Jam's own Datadog diagnostics (monitoring.jam.dev/logs).
  • Jam sets no cookies or localStorage on debate.pi-stack.org.
  • No console or network payload left the page in that window, but that isn't proof Jam never sends one before a recording.

SE-03 (Low): internal SEO note in the guide FAQ

  • Removed "Can Google index private DebateLab app pages?" from the shared FAQS in scripts/generate_seo_content.py and regenerated. No filler question replaces it.
  • It appeared on the 15 guides without their own FAQ (the QA report said 18). grep -rl "index private" public now returns nothing.

SE-04 (Low): pricing page price and the empty "Student plans" item

  • Already fixed on main. /debatelab-pricing on prod shows "$5/month + tax" and has no "Student plans" item (curl of prod; the item came from a dead base PAGES entry that seo_guides.py overrides).
  • I deleted that dead entry so it can't come back.
  • The guide copy now takes the price from one constant: PRO_PRICE (from site.json), used through a {pro_price} token in seo_guides.py and seo_answers.py. The rendered text is the same "$5/month + tax" the app uses.

SE-05 (Low): "11 prep modes" vs 8 in the app

  • The count now comes from the lists in tool-links.tsx: PREP_MODE_COUNT = Prepare + Practice = 8, and UTILITY_COUNT = 3.
    • Landing FAQ faq.a6: "It has {{modes}} prep modes". Rendered by both FAQ.tsx and the landing FAQPage JSON-LD in entry.tsx.
    • Landing pricing cards trialFeature2/proFeature2: "All {{modes}} prep modes and {{utilities}} utilities".
    • Landing JSON-LD featureList: built from the same three lists.
    • Generator: reads the keys out of tool-links.tsx and fails if it can't read every entry. Names come from en.json features.*. Feeds featureList, the llms.txt key facts and the {mode_count}/{mode_names}/{utility_names} tokens in the pricing and the two "vs" guides.
  • Removed features.subtitle ("Eleven debate tools…"). Nothing renders it.
  • "10 models" (QB-11) is left for T6.

AX-03 (Low): heading levels skip on guide pages

  • Already sequential on main. axe heading-order and page-has-heading-one pass with no violations on prod and on this branch's build for /, /privacy, /debatelab-pricing, /policy-debate, /debate-fallacies, /how-to-flow-in-debate, /ai-debate-practice and /best-ai-debate-tools. The QA builds came before the #short-answer H2 that now sits between the H1 and the highlight H3s.
  • To keep it that way, scripts/check-site.mjs now fails the build when a page's first heading isn't an H1 or a heading skips a level. All 48 built pages pass, and an injected H1→H3 on policy-debate fails as it should.
  • No class or style changes.

JSON-LD logo

  • The Organization logo is now https://debate.pi-stack.org/icon-1024.png:
    • in src/prerender/entry.tsx (T2 has merged, so this isn't waiting on it);
    • on the guide pages' Organization (author/publisher) in the generator.
  • curl https://debate.pi-stack.org/icon-1024.png returns 200 image/png, 279,465 bytes.
  • The JSON-LD parses in check-site. I didn't run the external schema.org validator.

Generated files

  • public/*/index.html (all 36 guides) and public/llms.txt were regenerated with python3 scripts/generate_seo_content.py, not edited by hand.
  • Every guide changes in its JSON-LD (the logo, plus a new featureList order on the software pages).
  • Dates: the 15 shared-FAQ pages pick up the new FAQS_DATE (2026-10-23). The pricing, vs-WinDebate and vs-DebateClub guides set modified 2026-10-23 because their copy changed. Every other guide keeps its date.
  • docs/operations.md and docs/testing.md describe the new tokens, FAQS_DATE and the heading check.

Open decisions (Adam)

  1. Jam user id. The policy describes today's behaviour, where the account ID is sent. If you switch Jam to a non-identifying id, replace the sentence "While you are signed in, your DebateLab account ID is available to Jam and is attached to any recording you make, which links the recording to your account." in p8 with:

    While you are signed in, Jam receives a random identifier that is not linked to your account, name or email address.
    The ES/HU sentence needs the same swap.

  2. Consent banner. None is added. Whether loading Jam for every visitor needs consent is your call.
  3. Jam sanitizers. jam-loader.ts configures no sanitizers or blur selectors. A recording's network log can include what the app exchanged, including request headers. Configuring sanitizers in Jam is worth considering. I left it out of the policy rather than promise it.

Verification

Full npm run verify, run locally because Actions minutes are out. This is a local pass, not CI. At d8adc643 (rebased on main b0b2ae79), it exited 0:

  • docs:check, lint (eslint, knip, repo rules) and typecheck: pass

  • npm test: 77 files, 564 tests passed

  • test:collaboration: 5 files, 30 tests passed

  • test:edge: 457 passed, 0 failed

  • build: prerender, sitemap (45 URLs), check-site: 48 HTML files, sitemap and llms.txt resolve; no problems, build boundaries

  • test:sw: upgradeActivated, firstVisitedRouteOffline, unvisitedRouteShowsRecovery, intentPrefetchedRouteOffline and offlineLogo all true

  • e2e smoke (desktop Chromium): 43 passed, 0 flaky

  • The first verify run, before the round-1 fixes, also exited 0, but two smoke specs were flaky and passed on retry: crossfire.spec.ts (grounded debrief) and first-run.spec.ts (tour tooltip). Neither touches anything this PR changes.

  • View-source checks on the build:

    • /: "It has 8 prep modes" in both the FAQ and the FAQPage JSON-LD, "All 8 prep modes and 3 utilities", logo icon-1024.png, no literal {{.
    • /privacy: 11 sections, dateModified 2026-10-23.
    • /debatelab-pricing, /policy-debate: checked.
  • I took screenshots of /privacy section 8 in EN, ES and HU from the local build and shared them in the T3 thread. GitHub has no CLI upload for them.

  • Not done:

    • nothing deployed;
    • JSON-LD not validated with Google's or schema.org's validator;
    • agentic tests not run (agentic/pages.e2e.ts covers /privacy in ES/HU and may comment on the English "Start Recording" label, which is quoted as Jam's button name).

No migrations. No Edge Functions to deploy.

Review rounds

  • Round 1 (fresh-context Claude Opus subagent, full diff vs main): no High or Medium findings. I fixed the cheap Lows:
    • tool-key regex hardened, with a count check;
    • one-item list naming;
    • "while you record" removed (a recording can hold earlier requests);
    • ES/HU quote "Start Recording";
    • ES word order;
    • Exa added to Data Sharing.
  • Left as Lows:
    • The ES/HU count phrasing ("Los {{modes}} modos", "Mind a {{utilities}}") reads correctly for today's 8 and 3, but the article would need adjusting at counts like 1 or 5.
    • Locale pricing strings still hard-code "$5" (in-app copy, T13's area).
    • The guides hard-code "3-day".

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Updates
    • Pricing and product descriptions now distinguish prep modes from utilities and reflect current tool counts and Pro pricing.
    • Updated debate guides with refreshed dates and organization branding; removed outdated FAQ entries about indexing private app pages.
  • Privacy
    • Updated the privacy policy to disclose Jam’s bug-reporting data collection and handling.
  • Documentation
    • Clarified site accessibility checks and guidance for keeping generated guide content current.

Programcode and others added 2 commits October 23, 2026 02:31
…E-03, SE-04, SE-05, AX-03)

- Privacy policy (EN, ES, HU): new section 8, "Bug Reports with Jam",
  describing what C1 ships: Jam loads for every visitor after the first
  interaction or 8 s, what its frame receives, what a recording contains,
  that the signed-in account ID is attached, the processor, retention, and
  how to object. Data Sharing now names Jam, PostHog and Sentry. Date moves
  to October 23, 2026.
- Guides: drop the internal "Can Google index private app pages?" FAQ.
- Prep-mode counts come from the tool lists in tool-links.tsx (8 modes,
  3 utilities) in the landing FAQ and pricing cards, the landing JSON-LD,
  the guides and llms.txt. The guide copy takes the Pro price from one
  constant, and the dead "Student plans" base entry for the pricing page
  is gone.
- check-site fails the build on a skipped heading level.
- Organization JSON-LD logo points at /icon-1024.png.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Data Sharing names Exa for web research.
- A recording can include anything exchanged with our servers, not only
  what happened while recording.
- ES/HU quote Jam's "Start Recording" button label; ES word order.
- The generator matches any key string and fails when it reads fewer
  keys than the list has entries; a one-item list names that item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 23, 2026 02:42

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 504ea07 and 7d65047.

📒 Files selected for processing: 51
  • docs/operations.md
  • docs/testing.md
  • public/ai-debate-case-generator/index.html
  • public/ai-debate-coach/index.html
  • public/ai-debate-practice/index.html
  • public/ai-rebuttal-generator/index.html
  • public/ai-vs-traditional-debate-prep/index.html
  • public/argument-flowchart/index.html
  • public/best-ai-debate-tools/index.html
  • public/british-parliamentary-debate/index.html
  • public/congress-debate/index.html
  • public/debate-block-file-guide/index.html
  • public/debate-clash-points/index.html
  • public/debate-collapsing-strategy/index.html
  • public/debate-evidence-finder/index.html
  • public/debate-fallacies/index.html
  • public/debate-judge-adaptation/index.html
  • public/debate-motion-analysis/index.html
  • public/debate-prep-time-management/index.html
  • public/debate-pronunciation-and-delivery/index.html
  • public/debate-rebuttal-practice/index.html
  • public/debate-speech-structure/index.html
  • public/debate-topics-for-students/index.html
  • public/debatelab-pricing/index.html
  • public/debatelab-vs-chatgpt/index.html
  • public/debatelab-vs-debateclub/index.html
  • public/debatelab-vs-windebate/index.html
  • public/how-to-cross-examine-in-debate/index.html
  • public/how-to-flow-in-debate/index.html
  • public/how-to-practice-rebuttals/index.html
  • public/how-to-prepare-for-a-debate-tournament/index.html
  • public/how-to-weigh-in-debate/index.html
  • public/how-to-write-a-debate-case/index.html
  • public/lincoln-douglas-debate/index.html
  • public/llms.txt
  • public/online-debate-timer/index.html
  • public/policy-debate/index.html
  • public/public-forum-debate/index.html
  • public/world-schools-debate/index.html
  • scripts/check-site.mjs
  • scripts/generate_seo_content.py
  • scripts/seo_answers.py
  • scripts/seo_guides.py
  • src/components/features/tools/tool-links.tsx
  • src/components/landing/FAQ.tsx
  • src/components/landing/Pricing.tsx
  • src/locales/en.json
  • src/locales/es.json
  • src/locales/hu.json
  • src/pages/PrivacyPage.tsx
  • src/prerender/entry.tsx

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

The changes add heading-order checks, derive preparation-mode and utility counts from tool lists for app and generated SEO content, and update published page metadata and FAQ content. They also add organization logos to structured data and expand translated privacy disclosures.

Changes: Product Counts and SEO Content

Layer / File(s) Summary
Tool counts in app content
src/components/features/tools/tool-links.tsx, src/components/landing/FAQ.tsx, src/components/landing/Pricing.tsx, src/locales/*.json, src/prerender/entry.tsx
The app derives preparation-mode and utility counts from tool lists. Landing-page translations and structured data use the derived counts and tool lists instead of fixed values.
SEO content generation
scripts/generate_seo_content.py, scripts/seo_answers.py, scripts/seo_guides.py, docs/operations.md
The generator derives tool labels and counts from app tool lists and fills price and tool placeholders. SEO answers and guides use those placeholders. The generator also applies a shared FAQ date rule to pages without page-specific FAQs.
Published SEO pages and site facts
public/*/index.html, public/llms.txt
Published pages update organization logos in structured data, dates, FAQs, and feature ordering. Pricing copy and llms.txt distinguish eight preparation modes from three utilities.

Heading Hierarchy Checks

Layer / File(s) Summary
Heading hierarchy validation
scripts/check-site.mjs, docs/operations.md, docs/testing.md
The site checker reports when the first heading is not H1 or when a later heading skips a level. The documentation includes these checks.

Privacy Policy Updates

Layer / File(s) Summary
Translated privacy disclosures
src/locales/en.json, src/locales/es.json, src/locales/hu.json, src/pages/PrivacyPage.tsx
The translated policies add disclosures about Exa, PostHog, Sentry, and Jam, update their dates, and renumber later sections. The privacy page renders 11 numbered sections.

Estimated code review effort: 3 (Moderate) | ~25 minutes

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

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

@Programcode

Copy link
Copy Markdown
Owner Author

Ready to merge from the code side, waiting on Adam's legal read.

Not merging until Adam has read the privacy-policy text in EN, ES and HU. The ES and HU translations are unreviewed. Adam also has three open decisions, listed in the description: the Jam user id, the consent banner and the Jam sanitizers.

@Programcode

Copy link
Copy Markdown
Owner Author

Synced with main. The new head is 7d6504798d0a0fc12aec8cb195d50c7d8fe58fbe. It's a normal merge of origin/main at 504ea07d: no rebase, no force-push, no amend.

How I got there. The PR branch already had a merge of main up to #211 (5120844a). I fast-forwarded to it and merged 504ea07d on top. That brought in #213, #214 and #215.

Conflicts. There was one, in docs/testing.md, in the npm run build paragraph. I kept main's sentence, which adds "the Spanish and Hungarian catalogs, and the signed-in layout" to the dependency-graph check. I also kept this PR's "(including heading order)" note for check-site.mjs.

Everything else merged cleanly:

  • docs/operations.md
  • src/components/landing/Pricing.tsx: main's bg-muted badge class plus this PR's mode counts
  • src/locales/en.json, es.json, hu.json

T11 left the catalogs at src/locales/{en,es,hu}.json, so this PR's keys stayed where they were. In each catalog, privacyPage has 25 keys (sections 1 to 11), faq.a6 and pricing.trialFeature2/proFeature2 carry {{modes}}/{{utilities}}, and features.subtitle is gone.

Guide pages. I re-ran python3 scripts/generate_seo_content.py on the merged tree and it changed nothing in public/.

Jam wording. It's unchanged from the approved PR: the user id is sent, there's no consent banner and no scrubbing change.

Full npm run verify on 7d650479, local, exit 0. This is a local pass, not CI, because Actions minutes are used up.

Step Result
docs:check pass
lint (eslint, knip, repo rules) pass
typecheck pass
npm test 81 files, 586 tests passed
test:collaboration 5 files, 30 tests passed
test:edge 457 passed, 0 failed
build prerender; sitemap 45 URLs (44 with lastmod); check-site: 48 HTML files, sitemap and llms.txt resolve; no problems; build boundaries pass
test:sw every check true: upgradeActivated; precachedRoutesOffline for /, /app, library, history, settings and timer; authCallbackNeverCached; timerRunsOffline; languageSwitchOffline; publicPagesKeepTheirOwnHtml; offlineIndicator; unvisitedRouteShowsRecovery; intentPrefetchedRouteOffline; offlineLogo. apiCacheEntries is 0.
e2e smoke (desktop Chromium) 47 passed, 0 flaky

Built output.

  • /: "It has 8 prep modes", "All 8 prep modes and 3 utilities", logo icon-1024.png, no literal {{.
  • /privacy: has "Bug Reports with Jam" and no literal {{.

Not used. The verify ladder doesn't need the local Supabase stack, so I didn't take the lock. No migrations or Edge Functions.

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.

2 participants