Skip to content

feat(analytics): wire built-in google_ads/meta_ads adapters to live clients (#120)#138

Merged
hyoshi merged 2 commits into
mainfrom
feat/analytics-builtin-live-wiring
May 23, 2026
Merged

feat(analytics): wire built-in google_ads/meta_ads adapters to live clients (#120)#138
hyoshi merged 2 commits into
mainfrom
feat/analytics-builtin-live-wiring

Conversation

@hyoshi
Copy link
Copy Markdown
Collaborator

@hyoshi hyoshi commented May 23, 2026

Summary

Follow-up to the protocol/registry PR. The built-in google_ads / meta_ads adapters previously returned empty tuples / stub headlines; this PR wires them to the real platform clients via a lazy-import fetcher layer.

Also fixes a silent-zero bug discovered during end-to-end validation: the aggregators assumed the live row shape (metrics nested under row["metrics"] for Google, conversions in row["actions"] for Meta) but BYOD clients return flat shapes — silently producing cost=0 / conversions=0 in BYOD mode.

What ships

  • mureo/analytics/builtin/_live_clients.pyfetch_*_metrics / fetch_*_performance_rows, lazy auth lookup, BYOD-routing via the existing _client_factory, uniform NoCredentialsError
  • Adapter changes: detect_anomalies + diagnose_performance now run against live (or BYOD) data; missing creds rendered as sentinel PerformanceDiagnosis ("credentials not configured"); type-safe shape-tolerance helpers
  • Regression tests pinning both shapes

Validated post-fix against the local BYOD bundle:

  • google_ads: 4 campaigns, spend 4,256,000, CV 478.4, CPA 8,895
  • meta_ads: 4 campaigns, spend 854,000, CV 306.6, CPA 2,785

Test plan

  • +26 tests (89 → 115 cumulative on analytics package)
  • Code-review HIGH (mock patch-site / dead except NoCredentialsError / asymmetric missing-creds) + MEDIUM (dedupe PerformanceFetcher, redundant byod_has, known-limit regression) addressed
  • Zero regressions vs main

Stacked on #PR1 (feat/analytics-module-protocol-120).

Refs #120

@hyoshi hyoshi force-pushed the feat/analytics-builtin-live-wiring branch from 467f2df to 895bacb Compare May 23, 2026 07:09
Base automatically changed from feat/analytics-module-protocol-120 to main May 23, 2026 07:14
… clients (#120)

Follow-up to the analytics-module Protocol PR. The built-in adapters
were registered with capability advertisements but their `detect_anomalies`
returned `()` and `diagnose_performance` returned a stub headline.
This PR wires them to the real Google Ads / Meta Ads clients via a
new lazy-import fetcher layer.

New: `mureo/analytics/builtin/_live_clients.py`
- `fetch_google_ads_metrics` / `fetch_meta_ads_metrics` — fetch the
  trailing-window + baseline performance reports, aggregate to
  account-level `CampaignMetrics`, feed the pure anomaly detector.
- `fetch_google_ads_performance_rows` / `fetch_meta_ads_performance_rows`
  — same fetcher shape for `diagnose_performance`.
- `_open_*_client` helpers centralise creds + BYOD routing; the
  factory layer (`mureo/mcp/_client_factory.py`) already chooses live
  vs BYOD so the fetcher delegates rather than duplicating the check.
- `NoCredentialsError` raised uniformly in live mode when creds are
  missing.

Adapter changes (`google_ads.py`, `meta_ads.py`):
- `detect_anomalies` defaults to the live fetcher; catches
  `NoCredentialsError` and returns an empty tuple (config error, not
  an anomaly).
- `diagnose_performance` defaults to the live performance-rows
  fetcher; catches `NoCredentialsError` and renders a sentinel
  `PerformanceDiagnosis` with a credentials-specific headline so the
  workflow can branch uniformly across both methods.
- Meta `diagnose_performance` adds a `_detect_cv_definition_mismatch`
  finding when an account mixes click-style and conversion-style
  result indicators across campaigns — preview of the deep CV-mismatch
  analysis surfaced by `meta_ads_insights_report`.
- `PerformanceFetcher` type alias deduped to `_common.py`.

Tests (45 new):
- `test_live_clients.py` — aggregation helpers, missing-creds raises,
  BYOD routing, sentinel rendering, plus a regression test pinning the
  Phase-1 known limitation (account-level aggregation can mask
  per-campaign anomalies).
- `test_diagnose_performance.py` — pure summariser shapes, CV-mismatch
  classifier precedence, adapter delegation through injected fetchers,
  sentinel headline on missing creds.

Coverage: 97.1% on `mureo/analytics/*`. Zero regressions vs the
prior branch. Lint clean.

Code-review findings addressed: HIGH (mock patch-site documentation,
dead `except NoCredentialsError`, asymmetric missing-creds semantics),
MEDIUM (duplicate `PerformanceFetcher` alias, redundant `byod_has`
check at fetcher layer, known-limitation regression test).

Refs #120
@hyoshi hyoshi force-pushed the feat/analytics-builtin-live-wiring branch from 895bacb to d128bb2 Compare May 23, 2026 07:14
@hyoshi hyoshi merged commit 13baf14 into main May 23, 2026
9 checks passed
@hyoshi hyoshi deleted the feat/analytics-builtin-live-wiring branch May 23, 2026 07:22
hyoshi added a commit that referenced this pull request May 23, 2026
…atforms (#120) (#142)

Bump version across pyproject.toml / mureo/__init__.py / .claude-plugin/plugin.json
and add the 0.9.9 CHANGELOG entry summarising the five PRs that shipped
the analytics-module work (#137, #138, #139, #140, #141).

Headline:

- opt-in `AnalyticsModule` Protocol + registry + entry-point group
  `mureo.analytics`, so external-integration platforms (official MCPs
  and third-party plugins) can plug into mureo's deep analytics on
  the same terms as the built-in adapters
- built-in google_ads / meta_ads adapters wired against live + BYOD
  clients with sentinel handling for missing credentials
- per-campaign fan-out for `detect_anomalies`, full implementations
  of `audit_creative` and `analyze_budget_efficiency`, and DEEP scope
  drilldown for `diagnose_performance`
- 10 row-shape TypedDicts re-exported from `mureo.analytics` for
  plugin-side typing
- skill prompts (daily-check, rescue, _mureo-shared) consult the new
  `mureo_analytics_modules_list` MCP tool and report
  `analytics_not_available_for_<platform>` honestly when a module is
  absent

Refs #120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant