Repository navigation
fix: reconnect the live plumbing (contact form, Writing, analytics) - #4
Merged
Merged
Conversation
The deploy build never received EMAILJS/reCAPTCHA/GoatCounter env vars, so the live contact form and analytics have been dead since deploy. Passes the existing GitHub secrets into the build step and fails loudly via a new check-build-env.js guard if any required one is missing. Also removes the misleading to_name/to_email EmailJS params (the template controls the real recipient) and stops rendering the chat widget when no chat API is configured, so it can't keep giving wrong offline answers until plan 06 replaces it. fetch-substack.js gets a browser-like User-Agent and a JSON API fallback for when the RSS route is challenged.
The CI fetch gets a 403 from Substack's GitHub Actions IP range, so the committed POSTS array is the reliable path — it's what ships whenever the cron/build fetch fails, which is currently always. Fetched from a residential connection per docs/plans/01-reconnect-live-plumbing.md.
Plan 07's test assumed hasPosts() was false; plan 01 commits the first real post, so /blog is now a genuine redirect target instead of the dangling-anchor case the old test guarded against.
- Decode HTML entities (numeric + named) in the RSS subtitle, not just — the live feed's em dashes were rendering as literal "—" on the one post this plan turns on. - Treat a bare [] from the JSON fallback as inconclusive (null), same as the RSS parser does for a body that isn't a real feed — this source has no way to confirm "genuinely zero posts" and shouldn't be allowed to empty the committed POSTS on a shape drift. - Collapse the guard + build into one env block in deploy.yml so a var added to only one of them can't silently pass the guard while missing from the actual build; add NEXT_PUBLIC_GOATCOUNTER_SITE to the required list (analytics was one of the three things this plan exists to fix). - Pair each fetch source with its parser directly (SOURCES tuples) instead of dispatching on a url.includes() check that could drift. - Route ChatBot through a client-only ChatBotGate using next/dynamic, so the stale offline-answer copy it ships is excluded from the bundle entirely while chat is unconfigured, not just unrendered. - Add the missing positive-branch test for hasPosts() in not-found-redirects.test.ts, and correct CLAUDE.md's ChatBot line.
This was referenced Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements docs/plans/01-reconnect-live-plumbing.md. Three things are dead on the live site today because the deploy build never receives the secrets they need:
NEXT_PUBLIC_EMAILJS_*isn't in the build env, so every submission gets "Email service not configured."POSTSwas empty; a real post ("The game had already started", 2026-08-04) now ships committed, since the CI fetch is blocked by Substack (403 on GitHub's IP range).NEXT_PUBLIC_GOATCOUNTER_SITEwasn't wired into the build either.A fourth item (chat widget) gets a stopgap here — plan 06 replaces it properly.
Changes
.github/workflows/deploy.yml— pass EmailJS/reCAPTCHA/GoatCounter secrets into a singleBuild Next.jsstep;scripts/check-build-env.jsfails the build loudly if a required var is missing (all in one env block, so guard and build can't drift).lib/services/emailjs.ts— drop the misleadingto_name/to_emailparams (the EmailJS template's own "To email" field controls the real recipient).app/layout.tsx/ newcomponents/chat/ChatBotGate.tsx— chat widget only mounts whenNEXT_PUBLIC_CHAT_API_URLis set, via a client-only dynamic import so the stale offline-answer copy is excluded from the shipped bundle entirely, not just unrendered.scripts/fetch-substack.js+ newscripts/lib/parse-substack-json.js— browser-like User-Agent, plus a JSON-API fallback source for when the RSS route is challenged. Fixed an entity-decoding bug found in review (numeric/named HTML entities like—weren't decoded, so the live post's em dash would have rendered as literal text).lib/content/writing.ts— committed the real fetched post (fetched from a residential connection; GitHub Actions' IP range is blocked).e2e/not-found.spec.ts/__tests__/not-found-redirects.test.ts— updated for the now-truehasPosts()state (plan 07's branded-404 tests assumed no posts existed).Before merging
This PR's own CI doesn't need secrets (by design —
ci.ymlbuilds without them so PRs from forks work). But merging tomainwill fail the deploy until these exist, because of the new guard step:.env.local(I can't read that file myself):{{to_email}}) — this PR removes the params that made that field work today. If it's still{{to_email}}, wire it to your address before merging, or the form will 422 for every visitor instead of just mis-routing.dommango.github.io, since this PR is the first time the service/template/public-key triple ships in a public bundle.Test plan
npx tsc --noEmit,npm run lint(2 pre-existing, unrelated errors inTravelMap.tsx/vitest.setup.ts— confirmed unchanged by this diff)npm test -- --run— 27/27 passnpm run build— confirmedout/index.htmlhasid="writing"and the real post text; confirmed the chat chunk is built but not referenced fromindex.htmlnpx playwright test— 25/25 passcurl -s https://dommango.github.io | grep -c goatcounter→ should be ≥ 1/dashboard-m7x9k2in a couple days for non-zero page views🤖 Generated with Claude Code