Engineering

Last Updated: August 25, 2026

This is the engineer writeup: how the site is built, how a change is tested, where it runs, how search engines and AI crawlers find public pages, and where to report a vulnerability. Live demos sit on How this site is built.

Last CI build

All 470 tests passing

Automated rating: WCAG 2.2 AA

View run
Passing tests470 / 470 · 100%
Coverage (npm run test:coverage)100% · 6629 / 6629 lines
WCAG 2.2 AA (axe-core)WCAG 2.2 AA · 13/13 public routes, axe-core

Aug 20, 2026, 20:16 UTC · 457/457 unit, 13/13 axe-core. Passing tests are Vitest + axe-core. Coverage is the Istanbul line rate from npm run test:coverage (CI fails under 100%). The WCAG bar is the last axe-core scan of public pages against 2.2 A/AA tags — an automated gate, not a certification.

How this site is built

Key points

  • Public pages are checked against WCAG 2.2 AA with axe-core in CI — failures block merge.
  • Who's Watching personas change shelves, hero stills, and landing pages.
  • Instagram, LinkedIn, and Meetup scrape on the server at ISR time.
  • Friend stays locked until an in-person QR or PIN unlock.

The lazy problem

A résumé site is a PDF with extra CSS. Recruiters want roles. Engineers want how it is built. Friends should not see hobbies next to my LinkedIn. I was tired of one page pretending to be all of them.

What it does

Who's Watching picks a persona. Home shelves, hero stills, and landing pages change. Friend stays locked until we meet. The host kiosk checks the scrapes so a dead cookie does not blank a projector. Public pages are gated in CI against WCAG 2.2 AA with axe-core.

How it works

  • Next.js Pages Router reads Sanity for copy, projects, and per-persona overrides (shelves, hero photo sets, Top 10).
  • Instagram, LinkedIn, and Meetup are scraped on the server at ISR time. Pages revalidate instead of fetching in the browser.
  • GitHub Actions runs axe-core at WCAG 2.2 AA on public pages. Failures block merge — accessibility is a pipeline, not a slide. CI/CD, Vercel, Sanity, and SEO (sitemap, IndexNow, llms.txt) are in this writeup.
  • Friend unlock is TOTP-style: a rotating QR and 6-digit code on the Clerk-gated host screen. Showing up is the auth factor.
  • The project stage hosts live demos — Perspective chat, a 3D recipe graph, generated snowflakes — next to these writeups.

Decisions I would defend

  • Personas live in localStorage, not accounts. Only the host signs in.
  • Scrape at build / revalidate time. A venue laptop should never be the crawler.
  • WCAG 2.2 AA is the CI bar, not a certification. axe-core catches labels, contrast, and target size on every PR.
  • Engineer case studies stay opt-in so a recruiter is not dropped into a graph.

CI / CD

Every push to main and every pull request runs GitHub Actions. The job uses Node 20, installs with npm ci, then fails the merge if any of these steps fail. The bars at the top of this page are the last published run. Passing tests are Vitest + axe-core. Coverage is the line rate from npm run test:coverage — CI fails unless that report stays at 100%. The last published run is rated WCAG 2.2 AA from axe-core (13/13 public routes, axe-core). That is an automated gate, not a certification.

  • Vitest unit tests
  • A full next build against the live Sanity production dataset
  • Playwright + axe-core on public pages, tagged WCAG 2.0 / 2.1 / 2.2 A and AA. Serious automated failures block merge.

In-progress runs on the same branch cancel so a fast follow-up commit does not wait on a stale check. Failed accessibility runs upload the Playwright HTML report as an artifact. Vercel ships the merged commit; preview deployments cover the PR.

Accessibility is a pipeline, not a slide. The public scan list includes home, watching, about, career, projects, blogs and a live post when Sanity has one, links, contact, recruiter brief, accessibility, privacy, terms, the branded 404, and this page. Host tools stay out of that gate. Details live on the accessibility statement.

Cloud

The app is Next.js 16 (Pages Router) on Vercel. Sanity is the public CMS. Clerk gates only the host surfaces. Pages revalidate every 60 seconds so copy, shelves, and scraped social stills refresh without a redeploy.

  • Vercel — production origin heyitsmicc.com, ISR, and a daily cron at 08:00 UTC
  • Sanity — production dataset for page copy, projects, blogs, and persona overlays. Studio is embedded at /studio
  • Clerk — host sign-in for the dashboard, LinkedIn session paste, and IRL Friend unlock. Visitors do not create accounts
  • ISR scrapes — Instagram, LinkedIn, and Meetup run on the server at build / revalidate time. A venue laptop is never the crawler
  • CDN images — Sanity, Instagram, LinkedIn, and Meetup hosts are allow-listed in Next image config

The host dashboard probes those dependencies so a missing cookie or CMS outage is visible before a projector goes live. Optional services (Perspective, Google Analytics) do not block the public site.

SEO

Public pages emit canonical URLs, Open Graph and Twitter cards, and JSON-LD for Person, WebSite, and breadcrumbs. Blogs add BlogPosting; the index is a CollectionPage. Private prefixes stay out of indexes: /api, /host, /studio, /sign-in, /sign-up, and /irl. Those routes also send X-Robots-Tag: noindex.

  • robots.txt allows public paths, including named AI crawlers, and points at the sitemap
  • sitemap.xml lists static routes plus Sanity blogs and projects, with image locs where they exist
  • IndexNow submits URL lists to participating engines. A public key file is rewritten to /api/seo/indexnow-key
  • Vercel Cron hits /api/seo/reindex daily. A Sanity webhook can use the same route after CMS edits
  • llms.txt and RSS give machines a digest without scraping the streaming UI. The feed includes an Atom self link to /feed.xml
  • /.well-known/security.txt (RFC 9116) publishes a contact for vulnerability reports, with a one-year Expires and a Canonical URL

Live files

  • Sitemap — Indexable URLs plus CMS blogs, projects, and image locs.
  • robots.txt — Public allow list, private disallows, and AI crawler allows.
  • security.txt — RFC 9116 contact for vulnerability reports.
  • llms.txt — Short machine-readable overview for search and AI systems.
  • llms-full.txt — Longer digest with project and blog links.
  • RSS — Blog feed for readers and aggregators.