Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion comfy_cli/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
# Only these events get the tracing_id --> workflow_run_id alias on PostHog.
EXECUTION_EVENTS = frozenset({"execution_start", "execution_success", "execution_error"})

# Namespace applied to event names on PostHog only, matching the
# app:/hub:/registry: surface-prefix convention in the shared project. Mixpanel
# keeps the bare legacy names (see ``mixpanel_name`` in track_event) so its
# historical streams stay continuous.
POSTHOG_EVENT_PREFIX = "cli:"

# Kwargs whose values must never reach tracking system.
# The key is kept (with a redacted marker) so we can still see whether the option was supplied.
SENSITIVE_TRACKING_KEYS = frozenset({"api_key"})
Expand Down Expand Up @@ -113,9 +119,11 @@ def track(self, event_name: str, distinct_id: str | None, properties: dict[str,
if not self.enabled or self.client is None or distinct_id is None:
return
merged = {**self._STANDARD_PROPERTIES, **properties}
# Membership check uses the canonical (unprefixed) name; the prefix is
# cosmetic to the PostHog taxonomy and applied only at capture time.
if event_name in EXECUTION_EVENTS and "tracing_id" in merged:
merged.setdefault("workflow_run_id", merged["tracing_id"])
self.client.capture(event=event_name, distinct_id=distinct_id, properties=merged)
self.client.capture(event=f"{POSTHOG_EVENT_PREFIX}{event_name}", distinct_id=distinct_id, properties=merged)

def flush(self) -> None:
if self.client is None:
Expand Down
25 changes: 22 additions & 3 deletions tests/comfy_cli/test_tracking_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,36 @@ def test_mixpanel_name_kwarg_routes_to_mixpanel_only(self, tracking_with_two_pro
_, mp_kwargs = mp_provider.client.track.call_args
assert mp_kwargs["event_name"] == "run"

# PostHog receives the canonical name, prefixed; Mixpanel keeps "run".
ph_kwargs = _posthog_capture_kwargs(ph_provider.client)
assert ph_kwargs["event"] == "execution_start"
assert ph_kwargs["event"] == "cli:execution_start"

def test_without_alias_both_providers_get_same_name(self, tracking_with_two_providers):
def test_posthog_prefixes_event_while_mixpanel_stays_bare(self, tracking_with_two_providers):
tracking_mod, mp_provider, ph_provider = tracking_with_two_providers
tracking_mod.track_event("execution_success")

_, mp_kwargs = mp_provider.client.track.call_args
ph_kwargs = _posthog_capture_kwargs(ph_provider.client)
# Mixpanel keeps the bare name for stream continuity; PostHog is namespaced.
assert mp_kwargs["event_name"] == "execution_success"
assert ph_kwargs["event"] == "execution_success"
assert ph_kwargs["event"] == "cli:execution_success"


class TestPostHogEventPrefix:
def test_top_level_event_is_prefixed(self, tracking_with_two_providers):
tracking_mod, mp_provider, ph_provider = tracking_with_two_providers
tracking_mod.track_event("install")

# Mixpanel bare, PostHog namespaced.
_, mp_kwargs = mp_provider.client.track.call_args
assert mp_kwargs["event_name"] == "install"
assert _posthog_capture_kwargs(ph_provider.client)["event"] == "cli:install"

def test_sub_namespaced_event_composes_with_prefix(self, tracking_with_two_providers):
tracking_mod, _, ph_provider = tracking_with_two_providers
tracking_mod.track_event("node:install")

assert _posthog_capture_kwargs(ph_provider.client)["event"] == "cli:node:install"


class TestProviderConstruction:
Expand Down
Loading