toolkit

GTM audit and fix · Playbook

GTM audit and fix full-audit evidence contract

Beru merges evidence; it does not invent browser observations or mutate GTM. Each optional input is a JSON object. Missing evidence is named as a limitation, never converted into a pass.

Page surface

Pass Ampharos’ normal ampharos.json unchanged with --surface-json. Beru reads pages[].url and pages[].tags[] (key, label, ids) to detect direct HTML installations that duplicate destinations configured in GTM.

Runtime journeys

--runtime-json accepts a shared capture envelope or a direct object:

{
  "source": "browser capture",
  "tool": "Chromium",
  "capturedAt": "2026-09-05T00:00:00Z",
  "settleMs": 12000,
  "artifacts": ["evidence/runtime-home.json"],
  "data": {
    "journeys": [
      {
        "name": "contact submit after consent",
        "url": "https://example.com/contact",
        "consent": "accepted",
        "action": "submit",
        "requests": [
          { "url": "https://www.google-analytics.com/g/collect?tid=G-XXXX&en=generate_lead" }
        ]
      }
    ]
  }
}

Keep capture provenance at the envelope root. Beru renders the source/tool, capture timestamp, settle window and raw-artifact paths so a reviewer can reproduce the claim instead of receiving an orphaned summary.

Use explicit consent states: pre-consent, rejected, or accepted. unknown is not treated as a pre-consent failure. Capture a fresh browser profile for consent tests and define the representative journey set before interpreting an absence.

Environment contract

--contract-json makes “wrong configuration” deterministic and permits intentional multi-property tagging without a false positive:

{
  "environment": "production",
  "tracking": "enabled",
  "allowed": {
    "gtm": ["GTM-XXXX"],
    "ga4": ["G-PRIMARY", "G-ROLLUP"],
    "ads": ["AW-123456789"]
  },
  "requiredEvents": ["page_view", "generate_lead", "phone_click"],
  "disallowedVendors": ["hotjar"]
}

An observed or configured identifier outside the relevant allowlist is a FAIL. Multiple identifiers inside an allowlist are intentional by contract, not “sprawl.” A required event absent from the supplied journeys is REVIEW because trigger coverage or implementation may be incomplete. A disallowed vendor that is observed firing is FAIL and, when the capture includes gtmTagId/gtmTagName, the remediation names the exact GTM object to pause. This is how “no longer used” becomes evidence—not by guessing from one quiet load.

Performance comparison

The same file may carry a recorded before/after baseline. Keep field and lab evidence separate:

{
  "before": {
    "desktop": {
      "lighthouse": 36,
      "verdict": "FAIL",
      "lab": { "lcpMs": 3800, "cls": 0.44, "tbtMs": 398, "lcpBreakdown": { "dominant": "element-render-delay", "ms": 1250 } },
      "field": { "rating": "good", "lcpMs": 2000, "inpMs": 100, "cls": 0.01 }
    },
    "mobile": {
      "lighthouse": 51,
      "verdict": "REVIEW",
      "lab": { "lcpMs": 19800, "fcpMs": 5300, "ttiMs": 21700 },
      "field": { "rating": "good", "lcpMs": 2000, "inpMs": 100, "cls": 0.01 }
    }
  }
}

Add after with the same shape after remediation. Beru calls out a healthy-field/slow-lab split instead of implying the synthetic worst case describes every real visitor. When BEFORE exists without AFTER, the report marks validation pending rather than making the baseline look like a completed fix.

Performance evidence may also carry ranked site-level opportunities that are outside GTM but explain the same trace. These are evidence inputs, not conclusions Beru invents:

{
  "source": "PageSpeed Insights",
  "toolVersion": "Lighthouse 13.4.1",
  "capturedAt": "2026-09-04T00:00:00Z",
  "artifacts": ["evidence/psi-desktop.json", "evidence/psi-mobile.json"],
  "opportunities": [
    {
      "priority": "high",
      "scope": "site performance",
      "title": "Optimize oversized third-party images",
      "evidence": "Approximately 40MB transferred from a development CDN in the recorded trace.",
      "change": "Move approved assets to the production CDN, compress them, and provide dimensions.",
      "verification": "Repeat the same profile and confirm lower transfer plus stable CLS.",
      "rollback": "Restore the prior asset URLs if delivery fails."
    }
  ]
}

Do not rephrase a Lighthouse opportunity as GTM causation. Scope it separately and retain the measured evidence that supports the rank.

For attribution, add the controlled comparison:

{
  "comparison": {
    "runs": 3,
    "sameUrl": true,
    "sameDevice": true,
    "enabled": { "metrics": { "lcpMs": 2500, "tbtMs": 500, "transferBytes": 900000, "requests": 80, "mainThreadMs": 1200 } },
    "blocked": { "metrics": { "lcpMs": 2100, "tbtMs": 250, "transferBytes": 600000, "requests": 60, "mainThreadMs": 700 } }
  }
}

Beru labels this a controlled comparison only when URL and device match and runs >= 3. Otherwise the delta is directional. Blocking all GTM measures the combined tag-manager ecosystem cost; isolate a vendor or tag separately before attributing the delta to that individual component.

Report order

The client-readable report leads with the recorded BEFORE baseline and optional AFTER validation, then field-versus-lab nuance, the browser-observed beacon matrix, measured GTM impact, and ranked remediation. Detailed container inventory remains supporting evidence rather than burying the decision.

Safe remediation

Recommendations contain priority, change, verification and rollback. Apply them with beru fix in a new workspace. Prefer pause-before-delete, preview every contracted journey, publish separately, and retain the prior live version for rollback. “Not observed” never authorizes deletion.