Skip to content

Commit 8e4a7fb

Browse files
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 #10176
1 parent 4736302 commit 8e4a7fb

21 files changed

Lines changed: 1636 additions & 238 deletions

.github/workflows/ui-deploy.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,14 @@ jobs:
3737
- name: Validate frontend
3838
env:
3939
VITE_LOOPOVER_API_ORIGIN: https://api.loopover.ai
40+
# PostHog web analytics (#8293). Inline rather than a repo secret, for the same reason
41+
# VITE_LOOPOVER_API_ORIGIN above is: this is a write-only ingest token that is embedded in
42+
# the client bundle and therefore public by design -- a secret would add ceremony while
43+
# protecting nothing.
44+
#
45+
# It MUST be set here, at build time: Vite substitutes `import.meta.env.VITE_*` when the
46+
# bundle is compiled, so setting this as a runtime Worker var/secret instead would leave
47+
# the shipped bundle with no token and turn every capture into a silent no-op with zero
48+
# errors anywhere.
49+
VITE_POSTHOG_PROJECT_TOKEN: phc_DmhFq8vjWoXGzd27EFp3sCFL4ugqXftaXhdy4E9noUZU
4050
run: npm run ui:openapi:check && npm run ui:lint && npm run ui:typecheck && npm --workspace @loopover/ui run build

.prettierignore

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Root prettier ignore -- DEFAULT DENY.
2+
#
3+
# Prettier governs only three workspaces in this repo (apps/loopover-ui,
4+
# apps/loopover-miner-ui, packages/loopover-ui-kit). Each has its own .prettierrc
5+
# and its own `format:check`, and `npm run ui:lint` -- which `test:ci` runs -- is
6+
# what enforces them. Everything else (src/**, test/**, scripts/**, the non-UI
7+
# packages, control-plane/**, review-enrichment/**) is hand-formatted at a much
8+
# wider effective line length and is NOT prettier input.
9+
#
10+
# Why this file exists: prettier resolves config by walking UP from each file, so
11+
# before this, a `prettier --write .` or a `prettier --write <backend file>` run
12+
# from the repo root found NO config, silently fell back to prettier's own
13+
# defaults (printWidth 80), and rewrapped thousands of lines of backend source
14+
# that nothing in CI had ever asked to be formatted. That produced enormous,
15+
# unreviewable diffs in otherwise small PRs -- repeatedly. Editors with
16+
# "format on save" hit the same trap.
17+
#
18+
# Default-deny rather than an explicit list of the excluded trees: a list goes
19+
# stale the moment a new top-level directory or package is added, and it fails
20+
# OPEN (the new tree gets mangled). This fails CLOSED -- a new directory is
21+
# ignored until someone deliberately un-ignores it below.
22+
/*
23+
24+
# The three prettier-governed workspaces, re-admitted explicitly. Un-ignoring a
25+
# path requires un-ignoring each parent directory first, hence the paired lines.
26+
!/apps/
27+
/apps/*
28+
!/apps/loopover-ui/
29+
!/apps/loopover-miner-ui/
30+
31+
!/packages/
32+
/packages/*
33+
!/packages/loopover-ui-kit/
34+
35+
# Never formatter input, even inside a governed workspace.
36+
#
37+
# These MUST be repeated here rather than inherited: prettier reads only ONE
38+
# ignore file per run -- the one for the directory it was invoked from -- so the
39+
# workspaces' own .prettierignore files do not apply to a root-level run. Without
40+
# these, `prettier --write .` from the root reformats exactly the files each
41+
# workspace deliberately exempts. Keep in sync with:
42+
# apps/loopover-ui/.prettierignore
43+
# apps/loopover-miner-ui/.prettierignore
44+
# packages/loopover-ui-kit/.prettierignore
45+
**/node_modules/
46+
**/dist/
47+
**/coverage/
48+
**/CHANGELOG.md
49+
**/.output/
50+
**/.vinxi/
51+
**/package-lock.json
52+
**/bun.lock
53+
**/pnpm-lock.yaml
54+
55+
# Generated -- reformatting these makes their own drift checks fail against a
56+
# file nobody edited (`ui:openapi:check`, `mcp:tool-reference:check`).
57+
**/routeTree.gen.ts
58+
apps/loopover-ui/.source/
59+
apps/loopover-ui/public/openapi.json
60+
apps/loopover-ui/src/lib/mcp-tool-reference.ts
61+
62+
# Deliberately formatter-free: prettier's mdx pass rewrites the template-literal
63+
# code inside <CodeBlock code={`...`}> attributes and destroys the embedded
64+
# YAML/compose indentation (#8182 fallout, repaired once already).
65+
apps/loopover-ui/content/docs/

apps/loopover-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"input-otp": "^1.4.2",
6969
"lucide-react": "^0.577.0",
7070
"motion": "^12.42.2",
71+
"posthog-js": "^1.409.3",
7172
"react": "^19.2.8",
7273
"react-day-picker": "^9.14.0",
7374
"react-dom": "^19.2.8",

0 commit comments

Comments
 (0)