Machina

Appendix B·Appendix

Annotated Code Artifacts

This appendix reproduces the code that carried the reconstruction, annotated. The excerpts are lightly trimmed for length and anonymized where a brand string would appear, but the logic is verbatim from the production change set. File paths are given so a reader can locate the analog in their own codebase. The chapters that argue for each change are cross-referenced.

The stack is Next.js 15 (App Router), TypeScript, Drizzle over PostgreSQL, and gtag.js plus the Measurement Protocol. None of these artifacts contains a brand identifier; the GA credentials live in environment variables (NEXT_PUBLIC_GA_ID, GA_API_SECRET, ADMIN_SECRET).


B.1 The server-side generate_lead, before and after

This is the artifact at the center of Chapter 6, The Server-Side Measurement Protocol. The first block is what the audit found. Read the host and the fallback before reading the fix.

Before (the defect):

// src/app/api/contact/route.ts  (as found)
if (process.env.NEXT_PUBLIC_GA_ID && process.env.GA_API_SECRET) {
  const clientId = attribution?.ga_client_id || `server.${Date.now()}`
  fetch(
    `https://www.google.com/mp/collect?measurement_id=${process.env.NEXT_PUBLIC_GA_ID}&api_secret=${process.env.GA_API_SECRET}`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: clientId,
        events: [{
          name: 'generate_lead',
          params: {
            currency: 'USD',
            value: 150,
            form_name: source || 'contact-form',
            service_type: service || 'general',
            source: attribution?.utm_source || 'direct',
            medium: attribution?.utm_medium || '',
            campaign: attribution?.utm_campaign || '',
          },
        }],
      }),
    }
  ).catch(() => { /* non-blocking */ })
}

Four defects sit in that block. The host is www.google.com, which returns 404 and records nothing. The client_id falls back to server.${Date.now()}, minting a fresh pseudonymous user on every cookie-less lead. The payload omits session_id and engagement_time_msec, so a hit that did land would attribute to (direct)/(none). The .catch(() => {}) swallows every failure, which is why a 404 ran silent for the property lifetime.

After (the fix):

// src/app/api/contact/route.ts

// Internal GA4/Ads lead-value weights (rough expected revenue per lead = job value ×
// assumed close rate). NOT customer pricing and never shown to users. PROVISIONAL.
const SERVICE_LEAD_VALUE: Record<string, number> = {
  termite: 400, fumigation: 500, commercial: 400, 'bed-bug': 250,
  rodent: 180, pest: 150, general: 150, ant: 100, 'wasp-bee': 100,
  other: 120, default: 150,
}

// ... inside POST(), after the lead row is written to PostgreSQL:
if (process.env.NEXT_PUBLIC_GA_ID && process.env.GA_API_SECRET && attribution?.ga_client_id) {
  const leadValue = SERVICE_LEAD_VALUE[service as string] ?? SERVICE_LEAD_VALUE.default
  fetch(
    `https://www.google-analytics.com/mp/collect?measurement_id=${process.env.NEXT_PUBLIC_GA_ID}&api_secret=${process.env.GA_API_SECRET}`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: attribution.ga_client_id,
        ip_override: ip,
        events: [{
          name: 'generate_lead',
          params: {
            currency: 'USD',
            value: leadValue,
            form_source: source || 'contact-form',
            service_type: service || 'general',
            engagement_time_msec: 1,
            ...(attribution.ga_session_id ? { session_id: attribution.ga_session_id } : {}),
          },
        }],
      }),
    }
  )
    .then(r => { if (!r.ok) console.error('[contact route] GA4 MP non-200:', r.status) })
    .catch(err => console.error('[contact route] GA4 MP failed:', err))
}

The guard changed from two conditions to three. The send now requires attribution?.ga_client_id to be present, which is the deliberate refusal to invent a user for an untrackable lead argued in Chapter 6. The host is corrected. ip_override: ip geolocates the conversion to the visitor. engagement_time_msec: 1 makes the event count in the session model. session_id is spread in only when it was captured. form_name became form_source to match the client parameter and the registered dimension (Chapter 8). The bare source, medium, and campaign parameters are gone, because GA4 reads event-scoped traffic parameters as manual overrides that corrupt attribution (Chapter 9). The response is inspected and logged (Chapter 13).

The careers route carried the same broken block under a different event name, career_application, which never matched the client career_application_submit. Rather than fix an orphan for a low-volume form, the reconstruction deleted the server send there and kept the client key event.


B.2 Reading client_id and session_id from the first-party cookies

The server holds no cookie of its own. The browser forwards the attribution object with the form POST, and the client reads the two identifiers out of GA4's own cookies. Chapter 2 explains the cookie formats; this is the parser.

// src/lib/utm.ts  (inside getAttribution(), called at submit time)
const gaMatch = document.cookie.match(/_ga=GA\d+\.\d+\.(\d+\.\d+)/)
if (gaMatch?.[1]) stored.ga_client_id = gaMatch[1]

// Session id from the _ga_<container> cookie so the server Measurement Protocol event
// stitches to the real session. Handles both GS1 (dot-delimited) and GS2 ($-delimited).
const sess = document.cookie.match(/_ga_[A-Z0-9]+=([^;]+)/)?.[1]
if (sess) {
  const sid = sess.includes('$') ? sess.match(/s(\d+)/)?.[1] : sess.split('.')[2]
  if (sid) stored.ga_session_id = sid
}

The _ga cookie holds GA1.1.<client_id>, where client_id is the two-segment value after the version prefix. The _ga_<container> cookie holds the session. Google shipped two formats: the legacy GS1.1.<sessionId>.<...>, where the session id is the third dot-delimited segment, and the newer GS2.1.s<sessionId>$o1$g0$..., where the session id follows the s. The parser branches on the presence of $. A generic _ga_[A-Z0-9]+ match avoids hardcoding the container id, so the same code works if the measurement id changes.


B.3 One page_view per route: RouteAnalytics

Chapter 8 treats the double-firing page_view as a taxonomy problem. The fix moves page-view emission out of the config and the per-page tracker into a single component mounted in the layout.

// src/components/ui/route-analytics.tsx
'use client'
import { useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { trackPageView } from '@/lib/analytics'

function derive(path: string) {
  if (path === '/') return { page_type: 'home' }
  const [a, b] = path.split('/').filter(Boolean)
  if (a === 'services' && b)  return { page_type: 'service',  service_slug: b }
  if (a === 'pests' && b)     return { page_type: 'pest',     pest_slug: b }
  if (a === 'locations' && b) return { page_type: 'location', location_slug: b }
  if (a === 'careers' && b)   return { page_type: 'careers',  job_slug: b }
  if (a === 'contact')  return { page_type: 'contact' }
  if (a === 'about' || a === 'team') return { page_type: 'about' }
  if (a === 'faq')      return { page_type: 'faq' }
  if (a === 'resources' || a === 'articles') return { page_type: 'resources' }
  return { page_type: 'other' }
}

export function RouteAnalytics() {
  const pathname = usePathname()
  useEffect(() => { trackPageView(derive(pathname)) }, [pathname])
  return null
}

The component derives page_type and the relevant slug from the URL path and fires one page_view on mount and on every usePathname change, which covers both the initial load and client-side navigations across the roughly 1,200 static pages. It works only in concert with two other changes: send_page_view:false on the config (below), and Enhanced Measurement page-change tracking switched off (Appendix A), so no automatic page_view competes with this one.


Chapter 10 covers Consent Mode; Chapter 8 covers the load-strategy and page-view flags. All three live in the inline bootstrap.

// src/app/(frontend)/layout.tsx
<Script
  src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
  strategy="afterInteractive"
/>
<Script id="ga-init" strategy="afterInteractive">
  {`window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}
    gtag('consent','default',{ad_storage:'granted',ad_user_data:'granted',ad_personalization:'granted',analytics_storage:'granted'});
    gtag('consent','default',{ad_storage:'denied',ad_user_data:'denied',ad_personalization:'denied',analytics_storage:'denied',region:['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','IS','LI','NO','GB','CH'],wait_for_update:500});
    gtag('js',new Date());
    gtag('config','${GA_ID}',{send_page_view:false})`}
</Script>

The first consent default grants every signal, which is the state for the US audience that makes up this property's traffic, so nothing changes for the real users. The second consent default, scoped to the 32 EEA, UK, and Switzerland region codes, denies storage until an update arrives. The order matters: the region-scoped default overrides the global default for a matching visitor. strategy moved from lazyOnload to afterInteractive so the tag loads right after hydration and sets the _ga cookie before a fast visitor submits a form, which lifts the ceiling discussed in Chapter 6. send_page_view:false hands page-view emission to RouteAnalytics.


B.5 Server Actions that guard themselves

The lead-outcome editor (Chapter 11) writes to the database through a Next.js Server Action. Route middleware protects page loads, but a Server Action is an independently addressable endpoint, so it verifies the session itself.

// src/lib/admin-auth.ts
import { cookies } from 'next/headers'

export async function verifyAdminToken(token: string | undefined): Promise<boolean> {
  if (!token) return false
  const secret = process.env.ADMIN_SECRET
  if (!secret) return false
  try {
    const dot = token.lastIndexOf('.')
    if (dot === -1) return false
    const expiry = token.slice(0, dot)
    const sig = token.slice(dot + 1)
    if (Date.now() > parseInt(expiry, 10)) return false
    const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret),
      { name: 'HMAC', hash: 'SHA-256' }, false, ['verify'])
    const sigBytes = Uint8Array.from(atob(sig.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0))
    return await crypto.subtle.verify('HMAC', key, sigBytes, new TextEncoder().encode(expiry))
  } catch { return false }
}

export async function requireAdmin(): Promise<void> {
  const token = (await cookies()).get('admin_session')?.value
  if (!(await verifyAdminToken(token))) throw new Error('Unauthorized')
}
// src/app/(admin)/admin/actions.ts
'use server'
import { db } from '@/lib/db'
import { contactSubmissions } from '@/lib/db/schema'
import { eq } from 'drizzle-orm'
import { revalidatePath } from 'next/cache'
import { requireAdmin } from '@/lib/admin-auth'

const STAGES = ['new', 'contacted', 'quoted', 'won', 'lost']

export async function updateLeadStatus(id: number, stage: string, jobValue: number | null) {
  await requireAdmin()
  if (!STAGES.includes(stage)) throw new Error('Invalid stage')
  const closed = stage === 'won' || stage === 'lost'
  await db.update(contactSubmissions)
    .set({
      stage,
      jobValue: stage === 'won' ? (jobValue ?? null) : null,
      closedAt: closed ? new Date() : null,
    })
    .where(eq(contactSubmissions.id, id))
  revalidatePath('/admin')
}

requireAdmin() reads the signed admin_session cookie and verifies its HMAC and expiry with the same scheme the middleware uses. The action calls it first, validates the stage against an allow-list, sets job_value only for won deals, stamps closed_at on a terminal stage, and revalidates the page. A reader porting this pattern should note the lesson: a Server Action is not covered by the route matcher that protects the page it lives on.


B.6 The outcome data model

The offline-revenue loop (Chapter 11) needs somewhere to record what became of a lead. The columns were added to the production database with an idempotent migration, then mirrored in the Drizzle schema.

-- production migration, applied with psql
ALTER TABLE contact_submissions
  ADD COLUMN IF NOT EXISTS attribution_wbraid varchar,
  ADD COLUMN IF NOT EXISTS attribution_gbraid varchar,
  ADD COLUMN IF NOT EXISTS lead_id            varchar,
  ADD COLUMN IF NOT EXISTS stage              varchar DEFAULT 'new',
  ADD COLUMN IF NOT EXISTS job_value          integer,
  ADD COLUMN IF NOT EXISTS closed_at          timestamptz;
// src/lib/db/schema.ts (Drizzle additions to contactSubmissions)
attributionWbraid: varchar('attribution_wbraid'),
attributionGbraid: varchar('attribution_gbraid'),
leadId:            varchar('lead_id'),
stage:             varchar('stage').default('new'),
jobValue:          integer('job_value'),
closedAt:          timestamp('closed_at', { withTimezone: true }),

Every column is nullable or defaulted, which makes the migration backward compatible: the running application ignored the new columns until the deploy that referenced them shipped, so there was no window where old code met a new schema and failed. attribution_wbraid and attribution_gbraid capture the iOS and Safari click identifiers that gclid alone misses, and the capture happens now so the history exists when Google Ads links later. lead_id is an opaque UUID for internal reconciliation, never an email or phone number.


See Appendix A for the property configuration these artifacts produced, and Appendix D for the Admin API calls that set the server-side configuration.