Frontend now drives Impressions / CTR / Spend from real per-variant
metrics (cycle_metric) rather than hardcoded zeros + fake +12% / -0.3%
deltas. Adds state.data.metricsByCycle keyed by cycle id; populated
via _fetchCycleMetrics which hits the panel-update endpoint directly
(no CMO chat dispatch needed — the route calls gRPC live per request).
After every cycles load (refresh-cycles or list-cycles ingest), fans
out one metrics fetch per cycle. _deriveKpis flattens all metrics
arrays, sums impressions/clicks/spend, computes real CTR from
clicks/impressions. Spend bar now reflects $spend / $budget_total
from real values instead of falling back to the $6,000 default floor.
Plugin route _real_payload_for gains a new branch:
adwright_get_cycle_metrics?cycle_id=N → core.get_cycle_metrics_tool(N).
Fake deltas dropped — real deltas need a previous-window snapshot
the backend doesn't supply yet.
Verified live: metricsByCycle.1 populated with backend's [{variant_id:1,
impressions:0, ...}] record. All metric values still zero in this
sandbox because no ads have dispatched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
routes/adwright.py now lazy-imports adwright_core from adwright-mcp/ and
calls the 6 v1 tool functions directly. Real payloads flow through
last-panel-update; mock data remains as fallback if adwright_core import
fails (proto version mismatch, missing adwright-mcp dir, etc).
Verified in webui's hermes-agent venv (Python 3.11 + protobuf 6.x):
- get_connections_status → real Plan B sandbox act_967504505966162
- list_cycles / list_segments / list_recipes → real (empty) data
- refresh_cycles → real timestamp + cycle re-fetch
Workspace-internal dependency on adwright-mcp/ documented in
CONNECTION-MAP (regen).
Karpathy 4 rules: smallest possible change to lift mock→real (single
function add + handler tweak), no abstraction layer introduced, fallback
preserves uptime if adwright-mcp evolves incompatibly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>