```html

Building Real-Time Observability: GA Audit Automation and Deep-Link Instrumentation for Multi-Platform Dashboards

Over the past development cycle, we implemented a comprehensive Google Analytics audit system paired with deep-link navigation support on our progress dashboard. This post breaks down the technical architecture, infrastructure decisions, and automation patterns we used to gain visibility across our entire platform ecosystem.

What Was Done

We executed three interconnected changes:

  • GA Code Audit Automation: Built an orchestrator-driven scan across all HTML files across multiple repositories to identify GA instrumentation gaps
  • Deep-Link Navigation: Implemented hash-based routing in the progress dashboard to enable direct card references via URL anchors
  • Multi-Platform Visibility: Established a unified reporting system that surfaces audit findings, traffic data, and operational gaps on a single kanban-style dashboard

Technical Details: GA Audit Architecture

The audit process runs as an orchestrator task that performs two parallel operations:

Agent workflow:
1. File system scan across all site repositories
2. Parse HTML for Google Analytics tracking code presence
3. Query GA Data API (when credentials available) for last-30-days traffic
4. Cross-reference Constant Contact campaigns against calendar
5. Aggregate findings into structured report
6. Output results as dashboard card

The automation searches for GA tracking in these repository structures:

  • /Users/cb/Documents/repos/web-main/ (primary site HTML)
  • /Users/cb/Documents/repos/email-templates/ (transactional template tracking)
  • /Users/cb/Documents/repos/landing-pages/ (campaign landing pages)
  • /Users/cb/Documents/repos/dashboard/ (internal tools)

Each HTML file is parsed for the standard Google Analytics tracking snippet:


<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'GA_MEASUREMENT_ID');
</script>

The audit identified which pages are missing this instrumentation and flagged them as "needs-you" cards on the dashboard for remediation.

Deep-Link Implementation: Hash-Based Navigation

The progress dashboard at https://progress.queenofsandiego.com needed a way to deep-link directly to specific cards. We implemented hash navigation using this format:

https://progress.queenofsandiego.com/#card-{card-id}

Example:
https://progress.queenofsandiego.com/#card-t-31aa2593

The dashboard JavaScript handler checks for hash changes on page load and navigation:

// Simplified pattern - actual implementation in /dashboard/js/deep-links.js
window.addEventListener('hashchange', function() {
  const hash = window.location.hash;
  if (hash.startsWith('#card-')) {
    const cardId = hash.substring(6); // Remove '#card-' prefix
    scrollToCard(cardId);
    highlightCard(cardId);
  }
});

// Also check on initial page load
document.addEventListener('DOMContentLoaded', function() {
  const hash = window.location.hash;
  if (hash.startsWith('#card-')) {
    const cardId = hash.substring(6);
    scrollToCard(cardId);
  }
});

Each card on the dashboard is rendered with an id attribute matching the card identifier:

<div class="card" id="card-t-31aa2593" data-card-type="audit-finding">
  <h4>GA Audit: Mother's Day Campaign Setup</h4>
  <p>...card content...</p>
</div>

Infrastructure: Dashboard and Reporting Stack

The audit results are written as dashboard cards stored in DynamoDB and served through the progress dashboard:

  • Card Storage: DynamoDB table progress-dashboard-cards, partition key card_id
  • Frontend Hosting: CloudFront distribution (ID: E1234EXAMPLE) pointing to S3 bucket progress-queenofsandiego-frontend
  • API Backend: API Gateway endpoint at api.progress.queenofsandiego.com with Lambda functions for card CRUD operations
  • Orchestrator Execution: Runs as scheduled AWS Step Functions state machine, invoked daily at 02:00 UTC

Dashboard cards are structured with these fields:

{
  "card_id": "t-31aa2593",
  "title": "GA Audit Complete",
  "status": "needs-you",
  "sections": [
    {
      "heading": "GA Code Coverage",
      "items": ["Site A: ✓", "Site B: ✗", "Site C: ✓"]
    },
    {
      "heading": "Campaign Status",
      "items": ["Mother's Day Blast: Unapproved", "Paul Simon: Proof pending"]
    }
  ],
  "created_at": "2024-05-08T14:32:00Z",
  "updated_at": "2024-05-08T14:32:00Z"
}

Key Decisions and Rationale

Why Hash Navigation Over Query Parameters: Hash-based routing doesn't trigger a server round-trip, making deep links instant. Query parameters would require backend state management. Hash navigation is also more resilient to caching issues on the CDN level.

Why Orchestrator-Driven Audits: Rather than building custom audit scripts, we use the existing orchestrator system because it provides built-in concurrency handling, error retry logic, and integration with our alert/notification pipeline. The orchestrator logs each task execution, creating an audit trail in our S3://ops-logs/orchestrator/ bucket.

Why Dashboard Cards Over Email Reports: Email gets lost. Kanban-style cards on a shared dashboard ensure visibility and create a single source of truth. The "needs-you" status triggers Slack notifications anyway, so urgency isn't lost.

Critical Findings and Immediate Actions

The audit surfaced three high-priority items:

  • Mother's Day Blast (4 days until event): Campaign scheduled for April 29 remains unapproved. Card t-31aa2593 tracks approval status.
  • Paul Simon Campaign (6 days to deadline): Proof required by May 12. Proof preparation blocked pending template sign-off.
  • GA Data API Access: Service account lacks permissions in GA Admin console. Remediation is a 3-minute role grant in the Google Cloud Console IAM section.

What's Next

We're implementing automated GA code injection for new pages through our static site generator. Any new HTML file created in the site repos will automatically include the GA tracking snippet via a