Make the privacy note and credits match the code

Claude Code inventories every data flow from the code, with file and line, rewrites the privacy note and credits to match, and leaves a review marker that CI warns about.

Claude Code intermediate Tested Opus 5.5 · Nov 2026May be dated

The prompt

Run it in Claude Code at the root of the repo before launch, and again whenever you add a provider, a cache, a font or a tracker.

Fill in the blanks3 placeholders · empty ones stay as {{…}}

Example

Input:

  • app: ., the repository root of a static prompt-library site (scripts/build.mjs renders every page into dist/, site/app.js is the only browser script, and Cloudflare serves the pages as static assets)
  • current_copy: none yet; the only privacy and licence claims are in README.md and the site footer that scripts/build.mjs writes
  • deployment_facts: none

Output (trimmed to three of the 25 inventory rows, two of the 19 mismatches, one line of the privacy note, the first code fix and two of the five marker and CI bullets):

# What Where it goes or is kept Trigger How long Evidence
R2 Search text typed on the home page written into the page address as ?q= with no request. From there it is in browser history, in the URL on a reload or shared link, and in the Referer of the next same-origin request to Cloudflare, because strict-origin-when-cross-origin sends the full URL within the site each keystroke (80 ms debounce) history: until cleared. Cloudflare: unknown app.js:123-128,137-143,148-152; build.mjs:396; dist/_headers:3
R5 The prompt text plus every filled-in placeholder value claude.ai (Anthropic), inside the link (/new?q= or /code?prompt=), in the visitor's own Claude session. Also stays in browser history a click on Open in Claude or Open in Claude Code (opens a new tab) Anthropic: unknown (its policy). History: until cleared build.mjs:68-72,217,260; app.js:190,249-259
R10 localStorage fill:<slug>, holding JSON of every placeholder value for that entry this browser each keystroke in a blank until every blank is empty or Clear all is used, or site data is cleared. No expiry app.js:174,182-193,195-198,204-208

…

  1. README.md:15. … "no tracking" overclaims: the code has no trackers and the CSP blocks third-party scripts (R4), but Cloudflare receives every request with IP and URL (R1), and host analytics are unknown (R14). I rewrote it to what the code proves.
  2. README.md:20. … "your values stay in your browser" overclaims: values are saved with no expiry (R10) and Open in Claude puts them in a claude.ai link (R5). I fixed the copy rather than the code, because sending them is what Open does.

…

  • AI providers: this site runs no AI. Open in Claude and Open in Claude Code open claude.ai in your own Claude account, with the prompt and your filled-in blanks in the link, which also stays in your browser history. From there Anthropic's privacy policy applies. Copy sends nothing.

…

  1. Optional: change the Referrer-Policy at build.mjs:396 from strict-origin-when-cross-origin to strict-origin. Moving between pages would then stop sending the previous address, including any ?q= search, to Cloudflare. Nothing on the site uses that header. A reload or shared link would still carry ?q=.
    • I disclosed this instead of changing the code, because no current promise is broken.
    • If you say go, I'll change the header and drop "the previous page's address" from the Hosting line.

…

  • Hidden on the page: the Markdown renderer prints raw HTML as visible text (build.mjs:45), so the build now strips HTML comments before rendering PRIVACY.md and CREDITS.md. In a test build, dist/privacy/index.html had no "privacy-review".
  • Check: scripts/build.mjs searches only PRIVACY.md. Under GitHub Actions it prints ::warning file=PRIVACY.md,line=1::still has its privacy-review comment. Do the check it describes, fix the note, then delete the comment.; elsewhere it prints warning PRIVACY.md: …. A missing file gives "is missing, so the site has no privacy note…". It never fails the build.

The absolute "your values stay in your browser" fails on two rows with evidence, saved with no expiry and carried to claude.ai by Open in Claude, and the run says it chose a copy fix over a code fix. It also traced typed search text into the address bar and the same-origin Referer, disclosed it and left the Referrer-Policy change waiting for a go-ahead; and because the renderer would print the marker as text, the build strips comments before rendering and warns with a GitHub Actions annotation, never a failure.

Why it works

  • Inventory before prose, on every path, with file and line for every row. Copy written from memory or from the old copy repeats the old claims. A row per data flow, with its evidence, makes each sentence checkable, and "unknown" stops the model filling gaps with what apps usually do. Fallback, error and cache-hit paths get their own rows because claims break there: "every image is licensed" fails on the placeholder path, and a shared cache keyed only on the query can serve one result with another's attribution.
  • Absolutes are false until a row proves them. The wrong sentences are nearly always "never", "only", "every" and "anonymous": "we never keep your address" fails when the host's request log or an abuse check keeps it. Hunting those words finds most mismatches fast. When a promise is worth keeping, fixing the code beats weakening the sentence, so the model has to say which it chose, and code fixes wait for your go-ahead.
  • Config and terms decide, so the copy follows them. Which AI provider runs, and on which tier, is a deployment setting: copy that names the default is wrong on every other deployment, so the copy reads the same config or states every case. Some wording is owed, not chosen (an ad network's cookie and consent notice, a provider's age limit), and citing the term that requires it stops "fewer words" from cutting it.
  • A marker the build warns about. The comment at the top of the note's source tells the reviewer what to check, and the build warns on every run until a person deletes it, or if the note goes missing. A warning, not a failure, keeps deploys moving. The check reads only the note's file, since a repo-wide search finds the check itself, and reads the shipped file only when it ships as written. When the build drops or escapes comments (in the example it would have printed the comment as text), strip it in the build and search the source.

Variations

  • Tighten pass. Once the facts are right, run: "Same facts and obligations in about half the words: one or two short sentences per credits section, the privacy note as a list."
  • Start every app flagged. Put the privacy page, marker included, in your project template, so every new app starts with a note that still needs its review.
  • Generated notices. Ask for a build plugin that walks the modules in the bundle, writes licenses.txt and warns when it can't find the licence of a package it has to credit, so the notices can't drift from the dependencies.
  • Generalised from an AI reference app and an app template.