Skip to content

fix: analytics only after consent; release anchors; CI hardening; tidy - #87

Merged
Bryan Fawcett (bryanfawcett) merged 5 commits into
stagingfrom
claude/blissful-bell-4liuye
Oct 13, 2026
Merged

Bryan Fawcett (bryanfawcett) merged 5 commits into
stagingfrom
claude/blissful-bell-4liuye

Conversation

@bryanfawcett

Copy link
Copy Markdown
Member

One commit per issue, against staging.

#83: Google Analytics loads only after consent

  • Analytics.astro no longer puts Google's tag on the page. One inline script queues the Consent Mode defaults (all denied), js and config, and defines window.nyuchiAnalytics.load(), which is now the only way the tag is requested. It is idempotent. The script calls it on page load only when nyuchi-consent parses, has v === CONSENT_VERSION, and has analytics === true.
  • CookieBanner.astro calls the loader from apply() only when analytics is granted, so it runs on Accept, on Save with Analytics ticked, or on boot with a stored yes. The legacy analytics-consent key is no longer migrated: it is ignored as an answer (the banner asks again) and is still removed on save.
  • New parseConsent() in src/data/consent.ts, used by the banner and by SupportWidget.astro. Older versions, unversioned records, non-objects and JSON that doesn't parse all count as no answer.
  • CSP: the two old GA inline scripts became one, so csp:fix leaves 4 hashes instead of 5. script-src, connect-src and img-src hosts are unchanged and already cover the dynamically added tag.
  • Wording: the analytics text lives in src/pages/legal/privacy.astro and cookies.astro, not in legal.ts. Both now say the analytics tag isn't fetched and Google Analytics gets nothing until you agree. They also cover withdrawal: the current page keeps the tag it already loaded, with cookies off, and later pages don't load it. Google Fonts is still named as a separate contact with Google. legal.updated and llms.txt are now dated 13 October 2026.
  • Tests: new tests/consent.test.ts checks that no built page has a googletagmanager script tag or preload. It also runs the built inline script in a vm sandbox: no load on a first visit, a stored no, an old version, an unversioned record, the legacy key or malformed values; exactly one load for a current yes or after the banner calls the loader. It also unit-tests parseConsent. tests/security.test.ts swaps the old "defaults before loader tag" ordering test for the new design: defaults before the loader, the banner calling the loader only under granted("analytics"), and the legacy key never read.
  • scripts/probe-consent.mjs now fails on any GA request before a choice or after Reject (across a reload). It plants an old-version record plus the legacy key and expects no request and the banner shown, and it expects the gtag request after Accept.

#85 (first part): anchors for test builds

  • 0.10.0's alsoIds now include 0.9.6, 0.9.7 and 0.9.8. The extension tagged v0.9.8 on 13 Oct, after the issue was filed, so I added it too. 0.10.0 stays coming.
  • taggedTestBuilds in src/data/releases.ts lists every tagged test build. A test in tests/seo.test.ts fails if any listed version has no anchor, or more than one. It's data-driven and makes no GitHub call. I checked that it fails when 0.9.8 is removed from alsoIds.
  • Not done here: the extension also has tags v0.8.14 to v0.8.31, which have no anchors (the page covers 0.8.3 to 0.8.13). I left them alone because they are outside this issue's scope.

#84: CI hardening

  • Every workflow sets permissions: {} at the top. Jobs set their own: contents: read for check and audit, and contents: write only for the two tagging jobs, unchanged.
  • The build workflow's checkouts set persist-credentials: false (neither job pushes).
  • actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 and actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0. Both are lightweight tags with no annotated tag to dereference, and each is what v4 currently points at.
  • nyuchi/.github reusable workflows were already pinned to commit 924e5b4, so I left them as they were.
  • New .github/dependabot.yml with a github-actions entry: weekly, grouped, cooldown.default-days: 7 (the org Semgrep rule requires it). There was no npm entry to keep.

#86: tidy

  • README, CONTRIBUTING and docs/toolchain.md are updated to match the current site:
    • the routes it has now
    • the staging → main flow
    • Vite+ as adopted
    • the inline scripts the CSP hashes
    • the current audit
    • the current repo layout
  • Removed the dead Lint badge, the vitest pin notes, master, "two pages" and "LICENSE if present". The stale vitest comment in build.yml is gone too.
  • react and react-dom removed. Nothing imports them, @bundu/ui@0.5.0 declares no peer dependencies, and the build and all checks pass. I regenerated the lockfile with the pinned npm 11.12.1 so the lockfile only loses the react entries, and checked that npm ci (npm 10.9.7) installs from it.
  • Prettier vs vp fmt: kept both. Each is run by a required org gate (the lint gate runs Prettier; vite-plus / check runs vp fmt --check). Removing either would break a gate this repo doesn't control. Their settings are already identical, and the new tests/toolchain.test.ts fails if .prettierrc and vite.config.ts's fmt block ever drift. The reasoning is written up in docs/toolchain.md.

Closes #83
Closes #84
Closes #86
Part of #85 (0.10.0 store status follows the release)

Checks run

  • npm test: build OK, 112 tests passed (6 files), CSP covers 4 inline scripts across 14 pages
  • npm run format:check: clean. vp fmt --check: clean
  • npx vp lint: 5 warnings, the same 5 as on staging; none new
  • npx astro check: 0 errors, 0 warnings, 0 hints
  • markdownlint-cli2 on CHANGELOG.md, README.md, CONTRIBUTING.md, docs/toolchain.md: 0 issues
  • yamllint (repo config) on .github/: clean. @action-validator/cli on all three workflows: clean. dependabot.yml validated against the SchemaStore schema. actionlint isn't installed here, so it didn't run
  • npm run consent:probe (Chromium): all 23 checks passed. That includes no GA request before a choice or after Reject, and none for an old-version or legacy consent. The gtag request is made after Accept. Not verified here: whether the GA cookie appears after Accept, because Google's bundle can't load in this sandbox (TLS proxy). Re-run against the deployed preview to close that.
  • Not verified in a browser: the deployed CSP. The local probe server doesn't send vercel.json headers.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq


Generated by Claude Code

Google's tag is no longer requested behind Consent Mode set to denied.
Analytics.astro queues the denied defaults and holds the only loader,
which runs on a stored yes from the current banner version or when the
banner records one. Older versions, the legacy key and malformed records
count as no answer, in the banner and the support widget alike. The
privacy and cookie policies now say Google Analytics contacts Google only
after you agree. Tests run the built script against each case; the
consent probe fails on any Google Analytics request before consent.

Closes #83

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq
The extension has tagged v0.9.6, v0.9.7 and v0.9.8 since the 0.10.0
entry was written, and their menus link to anchors the Releases page did
not have. They join 0.9.1 to 0.9.5 under 0.10.0, which stays "coming".
taggedTestBuilds lists every tagged test build, and a test fails the
build if one has no anchor or more than one.

Part of #85

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq
…ials

Workflows grant nothing at the top level and each job asks for its own
permissions: contents read for build and audit, write only for tagging.
The build workflow's checkouts no longer persist the token, since nothing
there pushes. actions/checkout and actions/setup-node are pinned to full
commit SHAs with the version in a comment, and Dependabot now keeps the
github-actions pins current. The nyuchi/.github reusable workflows were
already pinned to a commit.

Closes #84

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq
README, CONTRIBUTING and docs/toolchain.md now describe the Astro site's
pages, the staging to main release flow, Vite+ as adopted, the inline
scripts the CSP hashes and the current audit; the dead Lint badge, the
vitest pin, master and "two pages" are gone. react and react-dom are
removed: nothing imports them and @bundu/ui declares no peers. Prettier
and vp fmt both stay, since a required org gate runs each, and a test
fails if their settings differ.

Closes #86

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
learning Ready Ready Preview Oct 13, 2026 1:00am UTC

Request Review

…or-parser at 7.1.6+

npm audit fix brings http-cache-semantics past GHSA-ch52-4w7c-c8xp, so its
dated exception in scripts/audit.mjs goes. @tailwindcss/typography pins
postcss-selector-parser 6.0.10 (GHSA-rj75-hqrm-r3gf, moderate); an npm
overrides entry takes 7.1.6, and the built CSS is byte-for-byte unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq
@bryanfawcett
Bryan Fawcett (bryanfawcett) marked this pull request as ready for review October 13, 2026 01:01
@bryanfawcett
Bryan Fawcett (bryanfawcett) merged commit 1945708 into staging Oct 13, 2026
17 checks passed
@bryanfawcett
Bryan Fawcett (bryanfawcett) deleted the claude/blissful-bell-4liuye branch October 13, 2026 01:01

This branch was successfully deployed

1 active deployment
Preview — 78ee5015 Deployed Oct 13, 2026 by vercel[bot]
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