# Worked example: Acme Reports

Acme Reports is a fictional B2B SaaS product sold per workspace, with a marketing site at `www.example.com` (where the signup form lives) and the product at `app.example.com`. All identifiers are placeholders and all numbers are synthetic.

## Round 0: what the evidence showed before any question

- `www.example.com` theme header: `googletagmanager.com/gtag/js` with `G-XXXXXXXXXX` and a `gtag('config'` call, and `js.hs-scripts.com` with element `hs-script-loader`.
- `www.example.com` signup form template: `dataLayer.push` of `sign_up` after the form submits.
- `app.example.com` layout: `googletagmanager.com/gtm.js` with `GTM-XXXXXXX`.
- `package.json`: `@segment/analytics-next`, `posthog-js`.
- Billing webhook handler: calls `google-analytics.com/mp/collect`. `.env.example` lists `GA4_API_SECRET` (name only; `.env` was not opened; the scan used the exclusion pathspecs).
- dbt production artifacts from the last successful job run: marts `fct_subscriptions_daily`, `fct_workspace_usage_daily`, `dim_workspaces`; one semantic model with an `mrr` metric and a `cac_new_paid` metric.
- GTM container export (published version 42): a Google tag for the same `G-XXXXXXXXXX`, a GA4 event tag for `sign_up` triggered by the data layer event, a CMP template, a Google Ads conversion tag for `AW-XXXXXXXXX`.
- HubSpot property export: company property `last_active_date`, fill rate high, description empty.
- `docs/kpis.md`: a CAC definition with no owner or date.

Observed, then inferred and shown for confirmation: the signup push only reaches GA4 if GTM also loads on `www` (no `gtm.js` in the theme); if it does, `www` page views reach GA4 from two senders (the hardcoded gtag and the GTM Google tag); `sign_up` fires on form submit, not on account creation; a server-side purchase sender exists.

## Interview highlights

- Round 1: the monthly decisions are budget per paid channel and which trials sales calls. The disputed number is CAC: finance divides by first-paid workspaces, sales by closed-won opportunities, and a dashboard divides by `sign_up` events. Because the trigger is a metric dispute, round 4 runs for CAC in the first session.
- Round 2: GTM also loads on the marketing site, injected by the CMS's tag manager setting, which the repo scan could not see (confirmed by the web owner). Missing from the scan: Google Ads and LinkedIn ad accounts, a BigQuery export, and a reverse ETL sync from the warehouse to HubSpot. The Google Ads account is linked only under the agency's manager account, and one company role holds admin: two open items.
- Round 3: workspace ID is created by the backend; one person can belong to several workspaces; HubSpot companies map to workspaces through a synced `workspace_id` property. The CMP found in the container is confirmed; consent mode is advanced in the EU.
- Round 4: the head of growth chooses finance's definition for CAC and renames the sales version. `docs/kpis.md` gets a superseded notice pointing to `metrics.md`.
- Round 7: the GA4 reporting time zone changed from UTC to `America/New_York` on 2026-09-01. A theme release on 2026-09-14 added a hardcoded `gtag('event', 'sign_up')` beside the GTM `sign_up` tag, so `sign_up` was counted twice until that call was removed on 2026-09-21. The hardcoded `gtag('config'` call is still in the theme.

## Excerpt: stack.md

```markdown
### GA4 (production)
- Layer: destination
- Job: acquisition and on-site behaviour reporting; Google Ads key event import
- Status: active
- Operated by: analytics-lead; change approver: head-of-data; can publish: analytics-lead, agency (GTM only)
- Account owned by: company; company-held admin roles: 2 (analytics-lead, head-of-data)
- Linked under: none; access last reviewed: 2026-10-17
- Identifiers: measurement ID G-XXXXXXXXXX; property ID XXXXXXXXX; BigQuery export dataset analytics_XXXXXXXXX
- Data in: GTM web container on www and app (Google tag, sign_up event tag); hardcoded gtag config on www (second sender for page views, open item); Measurement Protocol from billing webhook (purchase)
- Agent access: Google's GA4 MCP server (read tools only, analytics.readonly scope); BigQuery execute_sql_readonly
- Credentials: GA4_API_SECRET in the production secret manager, owner platform-engineering
- Settings that change numbers: reporting time zone America/New_York from 2026-09-01 (D-20260901-1); consent mode advanced in EU (D-20261002-1)
- Last verified: 2026-10-17 by GTM export v42 and get_property_details; baseline: inventory/2026-10-17.yaml
```

## Excerpt: metrics.md

```yaml
metric_id: cac_new_paid_v1
display_name: CAC (new paid workspaces)
status: approved
last_reviewed: 2026-10-17
review_by: 2027-01-17
decision: Monthly budget allocation between paid channels
not_for: Judging sales team performance; use cost_per_closed_won_v1 for that
entity: workspace
grain: One workspace's first successful paid invoice
numerator: Acquisition cost for the month inside the cost boundary
cost_boundary: Google Ads and LinkedIn media spend plus agency fees, each fee allocated to the channel on the agency's statement; excludes salaries, software and content production
denominator: Distinct workspaces whose first successful paid invoice is in the month
time_basis: first_paid_at from billing
timezone: America/New_York
window: Calendar month in America/New_York, start inclusive, end exclusive
period_matching: Costs and first-paid workspaces are matched in the same calendar month; no lag
maturity: A month is reported only after its complete-data cutoff
complete_data_cutoff: All cost imports complete through month end plus two days
exclusions:
  - internal workspaces, flagged at creation
  - test workspaces, flagged at creation
  - reactivations (a workspace with an earlier paid invoice)
unknown_inputs: Workspaces with no captured channel stay in the denominator and are shown as "unattributed"
missing_source: Mark the month unavailable if any cost import is incomplete; never report a partial CAC
zero_denominator: null
segments_overlap: no (one channel per workspace)
aggregation: Sum costs and sum workspaces, then divide; never average channel CACs
time_additivity: Costs and counts add across months; CAC for a multi-month period is recomputed, never averaged
dimensions:
  channel: Workspace creator's first-user source and medium (GA4 user scope), stored on the workspace at signup, mapped to channel through the naming-rules.md channel map; fixed at signup
not_equivalent_to: [cost_per_closed_won_v1, cost per sign_up event]
implementation: dbt metric cac_new_paid in models/marts/finance.yml
business_owner: head-of-growth
technical_owner: analytics-engineering-lead
effective_from: 2026-10-01
```

Worked example, September 2026 (synthetic):

- USD 12,000 in the cost boundary. 200 `sign_up` events (including the double-counted week) give 60; 100 closed-won opportunities give 120; 80 distinct first-paid workspaces give 150. Expected answer: 150, with the other two named as different measures.
- By channel: paid search USD 7,200 and 40 workspaces (180); paid social USD 4,800 and 20 workspaces (240); organic and direct, no cost, 12 workspaces; unattributed, no cost, 8 workspaces. Costs sum to 12,000 and workspaces to 80, so the channel view reconciles to 150. The mean of the two paid channel CACs (210) is not a CAC.

Acceptance fixtures:

- Workspace A, first paid invoice 2026-09-15, channel paid search -> counted in September, paid search.
- Workspace B, first paid invoice at 2026-10-01T02:00Z -> 2026-09-30 22:00 in America/New_York, counted in September.
- Workspace C, paid in 2025, cancelled, paid again in September 2026 -> excluded as a reactivation.
- Workspace D, flagged internal at creation -> excluded.
- Workspace E, no captured source -> counted, unattributed.
- The billing webhook delivers Workspace A's first invoice twice -> counted once.

## Excerpt: data-dictionary.md

```markdown
## Misleading fields
- `last_active_date` on HubSpot companies: records the last CRM activity (email, call, meeting), not product use. For renewal risk use `fct_workspace_usage_daily`.

## Join hazards
- workspace -> users -> subscriptions: multiplies subscription revenue by user count. Aggregate subscriptions to workspace first, or use `fct_subscriptions_daily`.
```

## Excerpt: decisions.md

```markdown
### D-20260901-1: GA4 reporting time zone set to America/New_York
- Status: accepted
- Decision: change property time zone from UTC to America/New_York.
- Consequences: applies going forward only; daily series breaks on 2026-09-01; month-over-month comparisons across the change are annotated.
- Owner: analytics-lead; approved by: head-of-data on 2026-08-28

## Data incidents
- 2026-09-14 to 2026-09-21 | GA4 sign_up, cost per sign_up | theme release added a hardcoded gtag('event', 'sign_up') beside the GTM sign_up tag; call removed 2026-09-21 | treatment: annotated | record: none
```

## Tests added

- "What was CAC in September?" Pass: 150 from `cac_new_paid_v1`, with the September double counting noted as irrelevant because the denominator comes from billing.
- "What was CAC by channel in September?" Pass: the channel split above, with unattributed workspaces shown and the total reconciling to 150.

## What was proposed, not done

- Removing the hardcoded `gtag('config'` call from the theme: a change list for the web owner, not an edit.
- Moving the `sign_up` push to fire after account creation: handed to the tracking-plan-builder skill as a contract change.
- Open items with owners and due dates: the second page view sender on `www`; the agency-owned Google Ads link and the single company admin; the consent model outside the EU.
