# Session Handoff — Guilford Analytics (task #3) + engagement state

**Branch:** `claude/wonderful-hypatia-ap5jw1` (develop here). If the new session starts on a fresh
clone, `git checkout claude/wonderful-hypatia-ap5jw1` (it's even with `main` plus this doc).
**Why this exists:** the prior session wired GA4 credentials but its container couldn't see the new
env vars; a session restart is needed to pick them up. Everything needed to resume is below.

---

## ▶ First thing to do in the new session
Verify the GA4 service-account env vars are now visible (the user added them to Vercel **and** the
Claude session env):

```bash
node -e "console.log('email:', !!process.env.GA4_SA_CLIENT_EMAIL, '| key:', !!process.env.GA4_SA_PRIVATE_KEY)"
```

- **Both `true`** → proceed with the execution checklist below.
- **Still `false`** → the restart didn't propagate them; ask the user to confirm the exact names
  (`GA4_SA_CLIENT_EMAIL`, `GA4_SA_PRIVATE_KEY`) are saved to the Claude environment for this session.

---

## The task
**Task #3 "Define analytics goals, funnels & audiences"** (Guilford `website-and-analytics-refresh`),
M1 Discovery, `in_progress`, assignee **Tom Ellis**, **NOT blocked** ("Obtain Google Analytics
access" #1 is done). Feeds **Deliverable #2 "Analytics Measurement Plan"** (`not_started`, due
Jun 30). Related M4 task #13. **Chosen scope:** _framework + seed deliverable #2_ (full branded-PDF
plan is the Jun-30 follow-up). Default everything **internal**.

**GA4 credentials (provided):** property ID **`359308554`**; service account via
`GA4_SA_CLIENT_EMAIL` + `GA4_SA_PRIVATE_KEY` (set in Vercel Prod+Preview and the Claude env). The
key file stays off the repo.

**Governing inputs (locked):** audiences in **Ty's order, confirmed by Jean** — (1) prospective
students & families, (2) external: donors → alumni → wider community, (3) current students,
(4) faculty/staff. Build funnels/dashboards around **enrollment + giving conversions first**, keep
**current students** in view (**~60% of traffic is direct/repeat**), don't narrow the site. Kickoff:
prominent Give CTA, clear enrollment CTAs, seasonal flexibility, future personalization, automated
weekly/daily reports. Source: 6/16 kickoff notes + the task thread comment + the page-type inventory.

---

## Execution checklist
1. **Verify GA env** (above).
2. **Wire GA4 (small PR):**
   - `pnpm add @google-analytics/data`.
   - Implement `pullGA4Metrics()` in `src/lib/ga4.ts` (currently a stub that throws): build a
     credential from `GA4_SA_CLIENT_EMAIL` + `GA4_SA_PRIVATE_KEY` (normalize `\n` in the key) →
     `BetaAnalyticsDataClient` → `runReport` for sessions/users/conversions over a range → map rows
     to `GA4Metric[]`; return `null` on error (never block writes). `isGA4Configured()` already
     checks those two vars.
   - Set Guilford's `projects.gaPropertyId = 359308554` (admin, or a tiny migration like the prior
     Guilford ones).
   - `pnpm generate:types` if schema touched; typecheck; commit on the branch; PR to main.
3. **Pull the baseline** (one-off `pnpm payload run` script, or a temp script using the new helper):
   traffic mix, **new vs returning (confirm ~60% direct/repeat)**, top pages/sections, current
   conversions, mobile share, top sources, last ~90 days. Use it to set pre-launch benchmarks.
   - Note: GA Data API host (`analyticsdata.googleapis.com`) must be network-reachable — web egress
     worked in the prior session (we fetched guilford.edu), so this should be fine.
4. **Draft the framework** — delegate to the **`analytics-insights`** agent with a feedback-complete
   brief (locked audiences + kickoff + inventory + the baseline numbers). Framework contents:
   - **Goals→KPIs:** enrollment intent (Apply starts/submits, RFI, Visit) · giving engagement (Give
     CTR, donation start/complete) · wayfinding for all incl. current students · baseline + automated
     reporting to prove post-launch lift.
   - **Audiences→GA4 segments/audiences** in the locked order (build each for reporting + future
     personalization/remarketing).
   - **Conversions (GA4 key events):** `apply_start`, `apply_submit`, `request_info_submit`,
     `visit_scheduled`, `donation_start`, `donation_complete`; micro: `cta_click`, `program_interest`,
     `viewbook_view`, `email_signup`, `event_rsvp`, `video_play/progress`, `site_search`,
     `file_download`.
   - **Event taxonomy tied to the page-type templates** (the inventory synergy): one standardized GA4
     event schema baked into the shared component kit so events are consistent across P0+T1–T5 and the
     **T6 frame** (e.g. every CTA → `cta_click {cta_type,label,template,page_type,audience_priority,
     position,device}`; cards → `card_click`; listings → `filter_use`/`load_more`; forms →
     `form_start`/`form_submit`). Instrument once, globally.
   - **Funnels:** (A) Enrollment (entry→Academics/Program→Admissions→RFI/Visit/Apply→submit),
     (B) Giving (entry→Give CTA→giving landing→donation start→complete, **cross-domain**), (C) Visit,
     (D) RFI, (E) Current-student wayfinding.
   - **Cross-domain/technical:** GA4 cross-domain + referral exclusions for the apply portal,
     `giving.guilford.edu`/givecampus, `catalog.guilford.edu`, events platform — the **T6 frame** is
     the vehicle to inject a consistent GA tag/dataLayer on those non-Drupal systems; standardize via
     **GTM**. **Audit the existing GA4 first** (enhance, not rebuild); consent mode / ADA-privacy.
   - **Dashboards + automated reports:** Looker Studio dashboard by audience priority (enrollment ·
     giving · audience reach incl. % direct/repeat · content/top pages · wayfinding/search · mobile vs
     desktop); URL → project link library (kind `analytics`) + a deliverable `embedUrl` (live embed).
     Automated weekly+daily email via Looker scheduling; plus the portal `metrics` auto-pull once the
     seam is wired (today hand-entered — `src/fields/metrics.ts`).
   - **Baseline + lift:** benchmarks now; success thresholds for post-launch; annotate launch date.
5. **Record back** (portal **write MCP tools** — live, my session, no migration needed):
   - **Task #3** — `add_comment` (the framework + baseline highlights + the cross-domain/T6
     dependency); `set_task_status` → **done**.
   - **Deliverable #2** — `set_deliverable_status` → **in_progress**; `add_comment` seeding the
     structured plan (this framework as the working draft + Looker embed placeholder).
   - All **internal**; no client send / no `awaiting_client_approval`.
6. **Report** what moved + the 2–3 next actions (build full PDF measurement plan = deliverable #2,
   Jun 30; confirm cross-domain GA/GTM injection with IT — Gloria Thornton; GTM/event implementation
   in the build phase).

---

## Reference
- **Portal:** https://clients.ellisagency.ai/projects/website-and-analytics-refresh
- **IDs:** project slug `website-and-analytics-refresh`; task #3 (define analytics); task #13 (M4
  measurement plan); deliverable #2 (Analytics Measurement Plan); deliverable #6 (GA Implementation).
- **GA seam:** `src/lib/ga4.ts` (`isGA4Configured`, `pullGA4Metrics` stub) · `projects.gaPropertyId`
  (`src/collections/Projects.ts`, staff-only) · `src/fields/metrics.ts` (receipts the pull populates).
- **Agent:** `.claude/agents/analytics-insights.md`. **Inventory:**
  `artifacts/guilford-page-type-inventory.{md,pdf}` (rev 2: audiences locked, T6 added, non-Drupal
  out of scope). **Builder:** `scripts/build-inventory-pdf.mjs`.
- **Portal MCP write tools:** `add_comment`, `set_task_status`, `set_deliverable_status`,
  `create_task`, `draft_status_update` (load via ToolSearch `mcp__Ellis_Agency_Client_Portal__*`).

---

## Engagement state already shipped (merged to `main`)
- **Guilford project** set up (draft, internal): client, project, 5 milestones, ~19 tasks,
  6 deliverables, kickoff meeting + notes (PRs #44 and earlier).
- **Page-type inventory & template plan** delivered as an **Ellis-branded 12-page PDF** (rev 2);
  attached to the deliverable as media #6 (version 2). Inventory = 10 page types → **1 primary +
  5 Drupal templates (P0,T1–T5) + T6 portable frame** for non-Drupal systems.
- **Branded-PDF deliverable policy** → CLAUDE.md convention #12.
- **Vercel Blob store** switched private→**public** (the pinned adapter is public-only); file uploads
  + previews now work.
- **CSP fix** so in-browser PDF previews render (`frame-src` now includes `'self'`).
- **Known gotcha:** updating an existing `media` doc via the Local API re-triggers the storage
  adapter's blob upload, which fails on an existing blob ("blob already exists", no `allowOverwrite`).
  Flip media flags (e.g. `isCurrent`) via **raw SQL** in migrations. Editing media metadata in
  `/admin` would hit the same wall — fixable by bumping the storage adapter when deps are next touched.
- **CI note:** the GitHub Actions jobs have been failing with an instant "no runner" infra error
  (unrelated to code); **Vercel deploys are the real validation**. Worth checking org Actions
  minutes/billing.
