Commit 8e4a7fb
committed
feat(analytics): complete the PostHog migration — retire Umami, fix the MCP event contract
Umami is fully decommissioned and PostHog is the only analytics sink.
Web analytics (#8293, #8299). posthog-js replaces the hand-rolled Umami
beacon in routes/__root.tsx, behind the same first-party /stats proxy the
Umami collect endpoint used. lib/analytics.ts is the single seam; the SDK is
loaded via a dynamic import so an unconfigured build pays zero bytes, matching
browser-sentry.ts. Env is read at call time, not module scope, so the token
gate is stubbable. Umami parity was audited against the removed beacon's actual
payload: posthog-js supplies url/referrer/screen/language automatically,
page_title is added explicitly (it has no $title equivalent), geo still comes
from the proxy's x-forwarded-for, and respect_dnt replaces the beacon's own
doNotTrack check. persistence is "localStorage", not "memory" — memory resets
identity every reload and inflates unique visitors, the regression
JSONbored/metagraphed#8210 had to correct. cookieless_mode was rejected on its
documented behavior: it strips the IP before GeoIP runs, which would drop the
country data #8299 requires.
The proxy keeps its allowlist, one level looser (path prefixes, not exact
paths) so a posthog-js upgrade cannot silently break capture. Its original
justification was Umami-specific — that host served its admin API from the
collect origin — which does not carry over, but bounding a public
unauthenticated route still does. Session-replay snapshots get their own 2 MiB
ceiling so enabling #8295 cannot silently 413.
One behavior deliberately changes: the beacon fired once per page load and
never on SPA navigation, so pageview counts run legitimately higher after
cutover.
MCP telemetry (#10175). $mcp_tool_call was emitting LoopOver's own snake_case
property names under PostHog's reserved event name, so their built-in MCP
dashboards ingested every event and rendered every breakdown empty. It now
carries the canonical $mcp_* properties, with LoopOver's surface/transport/
category alongside them (PostHog's custom-server docs sanction extra
properties, and those three have no canonical equivalent). usage_event is
untouched — it stays the minimal, payload-free LoopOver event in its own
vocabulary, and the allowlist meta-test now checks the two vocabularies
separately.
Adds the two missing canonical events: $mcp_initialize, sourced from the
handshake's own clientInfo rather than the x-loopover-mcp-* headers that only
our published client sets, and $mcp_tools_list, reporting what this server
registered. Both carry $session_id from Mcp-Session-Id, which is what makes a
funnel across handshake, discovery, and tool calls possible at all. The closed
LoopOver error-code set is projected onto PostHog's closed $mcp_error_type set
so their error breakdown populates without losing the precise code.
Reshaping the wire format is free right now and stops being free immediately:
the project holds zero $mcp_tool_call events, because POSTHOG_API_KEY was never
set on the Worker.
Prettier (#10176). A root prettier run found no config, fell back to the
80-column default, and rewrapped backend source that nothing in CI formats —
repeatedly, inside unrelated PRs. Adds a default-deny root .prettierignore that
re-admits only the three governed workspaces, restates the generated-file and
content/docs exemptions that a root run cannot inherit, and pins ui-kit to its
current effective width so it is stated rather than inherited from a vendor
default. Verified by running prettier --write . from the root and confirming a
no-op. No existing code is reformatted.
Also formats two ui-kit test files that were already failing their own
format:check on main, which would otherwise have turned this PR red.
Closes #8293
Closes #8299
Closes #10175
Closes #101761 parent 4736302 commit 8e4a7fb
21 files changed
Lines changed: 1636 additions & 238 deletions
File tree
- .github/workflows
- apps/loopover-ui
- src
- lib
- routes
- packages
- loopover-contract/src
- loopover-ui-kit
- src/components
- src/mcp
- test/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
40 | 50 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
| 71 | + | |
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
| |||
0 commit comments