Skip to content

feat(telemetry): instrument the three unmeasured steps of the PLG funnel - #6115

Merged
viktormarinho merged 1 commit into
mainfrom
viktormarinho/plg-funnel-gaps
Aug 18, 2026
Merged

feat(telemetry): instrument the three unmeasured steps of the PLG funnel#6115
viktormarinho merged 1 commit into
mainfrom
viktormarinho/plg-funnel-gaps

Conversation

@viktormarinho

@viktormarinho viktormarinho commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Why

The PLG funnel dashboard maps 17 steps to events. Three had no event at all, so on the board a missing bar and a step nobody reached looked identical:

Step Status before
12 · Board opened no event
14 · Task marked done no event — task_run_completed is the agent's run finishing, not a person
15 · Limit banner shown no event — task_quota_blocked records the server-side block, not whether anyone saw the paywall

What

  • task_board_opened — ref callback on the board root, not an effect (banned by plugins/ban-use-effect.ts). A dataset flag guards a re-invoked ref from double-counting, mirroring trackGateRef in reports/auth-gate.tsx.
  • task_marked_done — fired inside the update mutation in useTaskBoardItemActions. That is the single choke point every status change routes through, so drag-drop, the bulk action bar and the task dialog are covered by one call site instead of ten scattered ones.
  • task_limit_banner_shown — in onDelegateError, where the paywall dialog is actually opened. Carries kind so the three refusal reasons stay distinguishable.

All three carry organization_id, consistent with the other org-scoped events.

Follow-up (not in this PR)

The funnel still cannot be a native PostHog funnel end to end, because the Studio server events key on organization_id while the engine events key on domain. Two things surfaced while measuring this:

  1. $process_person_profile: false silently drops group attachment. captureOrgEvent sets that flag when there is no user, and events sent that way arrive with $groups populated but $group_0 empty — so any unique_group math returns zero for them. task_run_enqueued and task_run_completed are affected; task_quota_claimed is affected only for its user-less half (67 of 84). Dashboard tiles were switched to count organization_id directly as a workaround.
  2. Studio does not persist an org's store domain. COMMERCE_DISCOVERY_BIND takes siteUrl per call and the mapping lives in the engine's database. Persisting it and having captureOrgEvent attach domain would let one native funnel span the whole flow — worth its own PR, since it needs a migration and touches the shared telemetry path.

Verification

  • bun run fmt, bun run check clean.
  • bun run lint: 0 errors (18 pre-existing warnings, none in the touched files).

No tests: these are telemetry call sites in React, which TESTING.md's two-tier policy puts outside unit tests, and the e2e suite is not set up to observe a PostHog capture over the wire.


Summary by cubic

Instruments three missing PLG funnel steps so funnel gaps are measurable. Previously these steps had no events; now we emit them without changing user flows.

  • task_board_opened: Fires once per mount via a root ref guarded by a dataset flag. Sends organization_id.
  • task_marked_done: Fires in the update mutation only when a person sets status to done. Sends organization_id and task_board_item_id. Not the agent’s task_run_completed.
  • task_limit_banner_shown: Fires when the subscription paywall dialog opens. Sends organization_id and refusal kind.
  • No API or UI changes; telemetry only.

Written for commit c2f5f1e. Summary will update on new commits.

Review in cubic

The PLG funnel dashboard had three steps with no event behind them, so a
missing bar was indistinguishable from a step nobody reached:

- `task_board_opened` — a ref callback on the board root, not an effect
  (banned), guarded by a dataset flag so a re-invoked ref can't double-count.
- `task_marked_done` — a PERSON moving a card to done. Deliberately not
  `task_run_completed`, which is the agent's run finishing. Fired in the
  `update` mutation, the single choke point every status change routes
  through, so drag-drop, the bulk bar and the task dialog are all covered by
  one call site instead of ten.
- `task_limit_banner_shown` — the subscription paywall actually rendering,
  next to the existing server-side `task_quota_blocked` (which records the
  block, not whether anyone saw it). Carries the `kind` so the three refusal
  reasons stay distinguishable.

All three carry `organization_id`, matching the other org-scoped events.

No tests: these are telemetry call sites in React, which the two-tier policy
in TESTING.md puts outside unit tests, and asserting a PostHog capture over
the wire is not something the e2e suite is set up to observe.
@viktormarinho
viktormarinho force-pushed the viktormarinho/plg-funnel-gaps branch from bd181ec to c2f5f1e Compare August 14, 2026 19:08
@viktormarinho
viktormarinho merged commit 6300bb0 into main Aug 18, 2026
24 checks passed
@viktormarinho
viktormarinho deleted the viktormarinho/plg-funnel-gaps branch August 18, 2026 10:55
decocms Bot pushed a commit that referenced this pull request Aug 18, 2026
PR: #6115 feat(telemetry): instrument the three unmeasured steps of the PLG funnel
Bump type: minor

- decocms (apps/api/package.json): 4.222.0 -> 4.223.0
- @decocms/native (apps/native/package.json): 4.222.0 -> 4.223.0

Deploy-Scope: web
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