Repository navigation
fix(web): gate CLERK_WEBHOOK_SECRET, honest signup count, media backfill reach - #211
Conversation
…olish Work in progress, captured before rebasing onto master (43 commits behind). - extension: web header + newtab polish, settings panel, manifest 0.1.18 - ui: add trackChannelClick / TrackChannel for chrome|telegram|email funnels - web: stop double-counting page views (gtag send_page_view: false) - web: header menu split (GetAIDRMenu, PhoneMenu, Compact/Wide rows) - verify-aidr skill: analytics + telegram features, refreshed driver - tests: feed-queries, tldr-section, chrome copy, campaign, analytics Excluded QA artifacts (.playwright-mcp/, design-preview.html).
Production probe: POST /api/webhooks/clerk returns 503
{"error":"clerk webhook not configured"} on every delivery, and
/api/system/accounts returns {"total":0,"status":"available"} — so /data
renders "Signups 0" for a mirror that has never received an event.
2b63ced shipped CLERK_WEBHOOK_SECRET as WORKER_OPTIONAL, so `pnpm sync-env`
silently skipped it. 8554148 had already added exactly this gate for the
sibling secret CLERK_SECRET_KEY (WORKER_REQUIRED + a fail-closed deploy
smoke); the new secret never got one.
- sync-env.ts: CLERK_WEBHOOK_SECRET -> WORKER_REQUIRED
- deploy-web.yml: fail-closed smoke asserting the endpoint is not 503/404,
mirroring the /__clerk/v1/environment gate. Asserts status only, never the
body. The probe posts a non-user event type, which the handler ignores, so
even a signature-verification regression could not write a clerk_users row
and inflate the public count.
- account-count.ts: an empty mirror is now `unconfigured` (total: null) rather
than a real 0. An empty table cannot distinguish "Clerk has no accounts"
from "no webhook delivery yet", and worker/README.md:93-96 already
specified `unconfigured` for "table missing / no rows yet" — the code had
drifted from its own documented contract. /data now says "Unavailable"
instead of a fabricated count, and becomes available once a row lands.
Verified: 1656 web tests, 65 extension tests, tsc --noEmit clean, biome clean.
Live gate confirmed red today (503) and passing for 400/401/405/200.
… image_url Closes the remaining half of #207. buildMissingMediaQuery gated candidates on `image_url IS NULL OR image_url = ''`, so every published row that already carried a legacy og:image was excluded — precisely the rows that most need a media_manifest, and the reason the backfill "cannot enrich rows with a legacy image_url". The only remaining gate is media_manifest being absent/empty/[]. `image_url` is still selected so the manifest can be built from it. The old predicate was pinned by an assertion in backfill.test.ts; it is replaced by a regression test asserting the clause stays gone and the column stays selected. The other half of #207 (the stale 0025 migration-gate assertion) already landed in cc460b3 and needed no change here.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Reviewer's GuideThe PR makes Clerk webhook setup fail closed, reports unknown rather than fabricated zero signups, and fixes media backfill reachability, while also adding cross-surface analytics/Telegram attribution, refreshing extension navigation and accessibility, and hardening hydration, feed, and verification tests. Review the deploy sequencing carefully: CLERK_WEBHOOK_SECRET must be provisioned and the Clerk endpoint registered before merging. Sequence diagram for Clerk webhook deployment gate and signup syncsequenceDiagram
participant Deploy as GitHub Actions deploy
participant Worker as Worker /api/webhooks/clerk
participant Clerk as Clerk
participant D1 as clerk_users
Deploy->>Worker: POST webhook smoke with clerk.webhook.smoke
alt CLERK_WEBHOOK_SECRET missing
Worker-->>Deploy: 503 clerk webhook not configured
Deploy-->>Deploy: Fail deployment
else Secret configured
Worker-->>Deploy: 4xx unsigned probe rejected
Deploy->>Clerk: Register user.created/user.updated/user.deleted endpoint
Clerk->>Worker: Signed user event
Worker->>D1: Sync Clerk user
end
Sequence diagram for cross-surface channel attributionsequenceDiagram
actor User
participant Surface as Web or Chrome extension
participant Analytics as track or trackChannelClick
participant Endpoint as /api/extension
participant GA as gtag
User->>Surface: Open campaign URL
Surface->>Analytics: track page_view and landing event
Analytics->>GA: Send sanitized event
User->>Surface: Click Chrome, Telegram, or Email link
Surface->>Analytics: trackChannelClick(channel, to)
Analytics->>GA: Send channel_click
Analytics->>Endpoint: GET with campaign-tagged parameters
State diagram for honest Clerk signup countstateDiagram-v2
[*] --> Unconfigured
Unconfigured --> Available: First Clerk user syncs
Available --> Available: Additional users sync
Unconfigured --> Error: Count query fails
Available --> Error: Count query fails
state Unconfigured {
[*] --> Unknown
Unknown: total = null
Unknown: status = unconfigured
}
state Available {
[*] --> Counted
Counted: total > 0
Counted: status = available
}
state Error {
[*] --> Failed
Failed: status = error
}
Flow diagram for media backfill candidate selectionflowchart LR
Item[Published item with summary] --> Manifest{media_manifest missing or empty?}
Manifest -->|No| Skip[Skip item]
Manifest -->|Yes| Input[Select image_url as manifest input]
Input --> Backfill[Build media manifest]
Backfill --> Persist[Persist enriched media_manifest]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Summary
Rebased onto
master(2b63ced, 43 commits) and fixes three defects found while auditing Clerk auth and the media backfill.This branch changes deploy behaviour — read the sequencing note before merging.
1.
CLERK_WEBHOOK_SECRETwas never gated (the root cause)Production probe:
2b63cedshippedCLERK_WEBHOOK_SECRETasWORKER_OPTIONALinscripts/sync-env.ts, sopnpm sync-envsilently skipped it.8554148had already added exactly this gate for the sibling secretCLERK_SECRET_KEY(WORKER_REQUIRED+ a fail-closed deploy smoke); the second Clerk secret never got one. The webhook fails closed before reading the body, so the only symptom was a silently dead signup sync while sign-in kept working.sync-env.ts:CLERK_WEBHOOK_SECRET->WORKER_REQUIREDdeploy-web.yml: fail-closed smoke asserting the endpoint is not 503/404, mirroring the/__clerk/v1/environmentgate. Status only, never the body. The probe posts a non-user event type, which the handler ignores — so even a signature-verification regression could not write aclerk_usersrow and inflate the public count.Verified red today (503) and passing for 400/401/405/200. Signup count confirmed unchanged at
0after probing.2.
/datarendered a fabricatedSignups 0/api/system/accountsreturned{"total":0,"status":"available"}for a mirror that had never received an event — indistinguishable from a real count on a public dashboard. An empty table cannot distinguish "Clerk has no accounts" from "no delivery yet", so an empty mirror is nowunconfigured(total: null) and the UI says "Unavailable".worker/README.md:93-96already specifiedunconfiguredfor "table missing / no rows yet" — the code had drifted from its own documented contract. This closes that gap.3. Media backfill could not reach rows with a legacy
image_url(closes half of #207)buildMissingMediaQuerygated onimage_url IS NULL OR image_url = '', excluding every published row that already carried a legacy og:image — precisely the rows needing amedia_manifest. Onlymedia_manifestgates now;image_urlis still selected as manifest input. The stale-predicate assertion inbackfill.test.tsis replaced by a regression test.The other half of #207 (stale 0025 gate name) already landed in
cc460b3.Rebase notes
8 conflicts, all resolved toward
master's current direction and verified individually:smoke.ts— tookmasterbyte-identical; the branch's naivebody.includes("/og-home.jpg")is subsumed bybb5c0cb's real<meta>lookup compared against theSITE_OG_HOME_IMAGE_URLconstant, plus a JPEG-magic-bytes fetch checknewtab.js— kept master's language-awaretagSiteLinks(document, {}, uiLang(settings)); folding the branch'strackChannelClickinto master's re-sorted import block avoided a duplicatetrackbinding (SyntaxError)GetAIDRMenu.tsx(add/add) —master+ threetrackChannelClickhandlers; preservedaa12d43(fix(web): remove mobile Get AI;DR trigger circle background #206) and080c9bbPhoneMenu.tsx—master's Radix dialog; the branch's hand-rolled portal/Escape/scroll-lock dropped as supersededNewsFooter.tsx— kept master'sgetCachedFeedFreshnessand the Plan 022: A rejected Telegram digest tail is not stored as sent #418 root-cause fix (useState<number | null>(null), cache read insideuseEffect), so master'ssuppressHydrationWarningband-aid is now removablefeed-queries.ts—master+ the 3-linesetLearnedKeywordsremoval, which is still correct:feed-cache.ts:86is the live path feeding highlight keywordsTwo stale source-grep tests were retargeted rather than deleted (
chrome.test.ts,chrome-copy.test.ts) — both pinned class/API names thatmasterrefactored while preserving the behaviour. Each now asserts the composition guarantee too.Verification
pnpm exec biome lint— 542 files cleanpnpm run check-types— exit 0node --checkon the merged extension bundleThis branch makes the deploy fail closed. If
CLERK_WEBHOOK_SECRETis still unset, the new smoke step fails and blocks the release. That is the intended trade (loud failure over a silently dead sync), but it means the secret must be pushed first:and the endpoint registered in Clerk → Webhooks against
https://aidr.today/api/webhooks/clerkforuser.created/user.updated/user.deleted.Test plan
CLERK_WEBHOOK_SECRETand register the Clerk endpointPOST /api/admin/clerk-synconce, then confirm/datashows a real count🤖 Generated with Claude Code
Summary by Sourcery
Harden Clerk signup synchronization and media backfill while making signup reporting honest and expanding cross-channel analytics, navigation, and verification coverage.
New Features:
Bug Fixes:
Enhancements:
CI:
Deployment:
Documentation:
Tests: