Files
veripath/docs/operations/decisions/DR-003-website-stack-fingerprinting.md
T

2.0 KiB

DR-003: Website Build-Stack Fingerprinting + SSR Signal

Date: 2026-08-17 Status: Accepted Supersedes: nothing (extends v1.1 capture contract)

Context

The Phoenix Salon + Spa audit (2026-08-17) shipped a finding — "no street address on /contact" — that was false: the address exists but is rendered client-side, so the pipeline's plain-HTTP capture did not see it and downstream analysis could not distinguish "content missing" from "content present but JS-rendered." The site was built on Lovable (Vite SPA behind Cloudflare); the capture did not record that, so the fix path ("one-time build fix" vs "edit content") had to be discovered by hand.

Decision

  1. multi_scraper.py records, at website capture time:
    • website.stack.detected / website.stack.primary — build-stack fingerprint (Lovable, v0, Next.js, Nuxt, Gatsby, Vite, React/Angular/Vue/ Svelte, WordPress, Squarespace, Wix, Webflow, Shopify, hosts).
    • website.stack.html_word_count / website.stack.ssr_signal — word count a non-JavaScript crawler reads from the raw HTML (weak < 50 words).
  2. SOP v1.2: the report states the build stack when known and flags weak SSR; capture-based website findings must be qualified by SSR signal.
  3. multi_scraper.py --selftest covers fingerprinting (Lovable fixture, weak vs present SSR) and runs in CI-free environments without network.

Rationale

Knowing how a site is built determines (a) what the capture can and cannot see, and (b) the cheapest correct fix. Regex fingerprinting on raw HTML is deliberate: known-site audits need a label, not a scanner — false negatives land in unknown, never a confident wrong answer.

Verification

  • --selftest: PASS (Lovable fixture → primary=lovable, ssr=weak; 80-word page → ssr=present, primary=unknown).
  • Live re-capture of phoenixcameronpark.com: detected=['lovable'], html_word_count=13, ssr_signal=weak — matches the manual Playwright audit of the same day.