Skip to content

feat(admin): add Plans analytics dashboard - #2976

Open
WcaleNieWolny wants to merge 17 commits into
mainfrom
wolny/plans-analytics-dashboard
Open

feat(admin): add Plans analytics dashboard#2976
WcaleNieWolny wants to merge 17 commits into
mainfrom
wolny/plans-analytics-dashboard

Conversation

@WcaleNieWolny

@WcaleNieWolny WcaleNieWolny commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a read-only admin Plans analytics page with traffic, billing-state, and checkout-intent charts
  • reconstruct historical billing state at each UTC Plans opening and attribute checkout starts within 24 hours
  • add bounded PostHog queries, structured failure/data-quality reporting, accessible chart alternatives, and deferred checkout-completion documentation

Measurement semantics

  • range-wide unique organizations count on their first opening inside the selected range
  • daily billing breakdown counts each organization once per UTC day at its first opening
  • checkout starts match the latest preceding Plans opening within 24 hours and are attributed to that opening day
  • legacy pathname reconstruction is fail-closed because production evidence was unavailable; the unvalidated deduplication threshold is reported as null

Test plan

  • run frontend and backend lint
  • run focused Plans analytics, PostHog transport, billing-history, admin endpoint, and chart tests
  • run the complete unit-test suite
  • run the production frontend build
  • run frontend typecheck
  • run backend typecheck without inherited main-branch email-preferences errors
  • verify no customer-facing Plans banner, plan-selection, or ended-subscription files changed

Screenshots

Authenticated local visual verification is unavailable because the local Supabase authentication service is not running. The page layout, states, accessible chart tables, UTC labeling, and safe external link are covered by focused behavior and wiring tests.

Checklist

  • admin dashboard remains read-only
  • PostHog response bodies and organization fan-out are bounded
  • legacy reconstruction remains disabled until event-time evidence is validated
  • all user-facing copy uses translation keys
  • checkout completion remains explicitly deferred
  • no customer Plans banners or ended-subscription behavior changed

Out of scope

  • customer-facing Plans banners and ended-subscription behavior
  • checkout completion until a reliable server-side completion event exists

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b598a47b-2088-4be6-9653-9ced145e0950

📥 Commits

Reviewing files that changed from the base of the PR and between 98fc5e3 and 86bb39d.

📒 Files selected for processing (7)
  • docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
  • docs/superpowers/specs/2026-08-10-plans-analytics-dashboard-design.md
  • messages/en.json
  • supabase/functions/_backend/utils/plans_analytics.ts
  • supabase/functions/_backend/utils/plans_billing_history.ts
  • tests/admin-plans-analytics-dashboard.unit.test.ts
  • tests/plans-analytics-orchestration.unit.test.ts
📝 Walkthrough

Walkthrough

Adds a read-only admin Plans analytics dashboard. It reconstructs visits and checkout intent from PostHog, classifies historical billing states, exposes quality metadata, and renders localized accessible charts with loading and failure states.

Changes

Plans analytics dashboard

Layer / File(s) Summary
Dashboard design and contracts
docs/superpowers/plans/..., docs/superpowers/specs/..., docs/admin/plans-checkout-completion.md
Documents the architecture, response contract, data-quality rules, verification requirements, and deferred checkout-completion analytics.
Shared PostHog transport
supabase/functions/_backend/utils/posthog_read.ts, supabase/functions/_backend/utils/builder_analytics.ts, tests/posthog-read.unit.test.ts
Adds typed HogQL results, bounded response handling, timeout classification, and shared transport use.
Event model and billing history
supabase/functions/_backend/utils/plans_analytics_model.ts, supabase/functions/_backend/utils/plans_billing_history.ts, tests/plans-analytics-model.unit.test.ts, tests/plans-billing-history.unit.test.ts
Reconstructs logical Plans openings, attributes checkout starts, aggregates UTC metrics, and classifies historical billing states.
Backend aggregation and endpoint wiring
supabase/functions/_backend/utils/plans_analytics.ts, supabase/functions/_backend/private/admin_stats.ts, src/stores/adminDashboard.ts, tests/plans-analytics-orchestration.unit.test.ts, tests/admin-stats.unit.test.ts
Adds bounded queries, orchestration, data-quality metadata, billing-history integration, and the plans_analytics metric category.
Admin frontend
src/services/adminPlansAnalytics.ts, src/pages/admin/dashboard/plans.vue, src/constants/adminTabs.ts, messages/en.json
Adds response validation, chart-series mapping, navigation, localized states, refresh coordination, and accessible chart cards.
Accessible stacked charts
src/components/admin/AdminStackedBarChart.vue, src/components/admin/adminStackedBarChart.ts, tests/admin-stacked-bar-chart.unit.test.ts
Adds optional theme-aware dataset borders for stacked bar charts and tests the enabled and disabled behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: codex

Suggested reviewers: riderx

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding an admin Plans analytics dashboard.
Description check ✅ Passed The description includes the required summary, test plan, screenshots, and checklist sections with specific implementation details and test results.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: medium. Left a non-blocking comment; not approving because this exceeds the low-risk auto-approval threshold (large admin Plans analytics change with billing-history reconstruction and private admin API surface). Cursor Bugbot was not present after the initial check poll, so that signal was skipped; human review is needed and a reviewer will be assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor
cursor Bot requested a review from riderx August 10, 2026 08:49
@coderabbitai coderabbitai Bot added the codex label Aug 10, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing wolny/plans-analytics-dashboard (86bb39d) with main (e745fdd)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: medium. Left a non-blocking comment; not approving because this exceeds the low-risk auto-approval threshold (large admin Plans analytics change with billing-history reconstruction and private admin API surface). Cursor Bugbot was not present after the initial check poll, so that signal was skipped; human review is needed and riderx is already assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 21

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md`:
- Around line 1393-1398: Update the validation commands in the “Step 1: Run
formatting and lint first” section to use the repository-required commands `bun
lint:fix` and `bun lint:backend` instead of the `bun run` variants.
- Around line 1234-1235: Update the planned “plans-analytics-posthog-timeout”
translation to use the design-approved message: “This range was too large to
process. Select a shorter period and try again.” Keep the separate
“plans-analytics-range-too-large” translation unchanged.
- Around line 1356-1362: Update the fifth card implementation to use the defined
translation keys for its title, deferred description, and external-link label
instead of literal user-facing text. Keep the checkout-completion URL literal,
render the card full-width, and preserve the external link’s new-tab behavior
with rel="noopener noreferrer".
- Around line 271-272: Update the PostHog configuration around POSTHOG_READ_HOST
and POSTHOG_READ_PROJECT_ID to require both environment variables when
POSTHOG_READ_KEY is present; remove the hard-coded host and project fallbacks,
and preserve the existing behavior only when all required configuration is
supplied.
- Around line 402-423: Extend the test named “keeps range-wide uniques distinct
from daily uniques and reconciles graph totals” with multiple checkout-start
events for the same organization on one day, including different billing states
at their attributed openings. Assert Graph 3 deduplicates the organization and
Graph 4 selects the earliest attributed checkout, while preserving the existing
traffic and reconciliation assertions.
- Around line 737-756: Update loadPlansBillingHistories orchestration to
validate each orgIds entry as a UUID before executing the ANY($1::uuid[]) query;
exclude invalid IDs from the database request and add them to
excludedMissingOrganization, while preserving valid IDs for billing-history
loading.
- Around line 947-965: Update the SQL WHERE clause to apply event-specific
windows: retain User visit rows only when timestamp is in [queryStart, end), and
retain Checkout Started rows only when timestamp is in [start, queryEnd).
Replace the shared timestamp bounds while preserving the event filter and
half-open interval semantics.
- Around line 1102-1147: Expand the admin Plans analytics test coverage beyond
adapter output and source-text assertions by exercising the page state machine
and rendered behavior. Add tests for response validation, loading, valid empty
data, partial billing warnings, timeout, unavailable, and too-large states, plus
UTC display and link rendering; use the page’s existing fetch/rendering symbols
and preserve the current adapter and wiring checks.
- Around line 134-139: Update the histogram query’s window partitioning around
lagInFrame to use the same normalized runtime identity key as the legacy repair
logic—orgId combined with sessionId when present, otherwise actorId—instead of
partitioning by org_id and distinct_id separately. Ensure the 30-second
threshold is validated per normalized session-or-actor identity before enabling
legacy repair.
- Around line 914-917: The contract constants must keep legacy reconstruction
fail-closed: change LEGACY_PATH_SOURCE from 'event' to 'unavailable' and
represent the threshold as null wherever this contract is declared or used. Only
permit switching to an event-based source after successful evidence validation
and accompanying tests, while preserving the existing safeguards against
fabricated or duplicate visits.
- Around line 517-523: Update the legacy candidate predicate in the analytics
reconstruction plan to require page !== 'plans' alongside the existing legacy
event and normalized-path checks. Keep the exact candidate predicate unchanged
so events matching page === 'plans' are classified exclusively as exact.
- Around line 1295-1300: Update loadPlansAnalytics to pass the result of
adminStore.fetchStats('plans_analytics') through parsePlansAnalyticsResponse
before assigning it to data.value, and remove the unchecked cast. Preserve the
existing loading and error-handling flow while ensuring only validated
PlansAnalyticsResponse data reaches the chart adapters.

In `@docs/superpowers/specs/2026-08-10-plans-analytics-dashboard-design.md`:
- Around line 181-205: Align the PlansAnalyticsResponse contract with the
consumer’s established type names by replacing DailyBillingSeries and
DailyCheckoutIntentSeries with DailyBillingPoint and DailyCheckoutIntentPoint,
or explicitly defining aliases for the existing names. Apply this consistently
to visitorBreakdown, checkoutIntent, and checkoutVisitorBreakdown so the wire
contract is unambiguous.

In `@supabase/functions/_backend/utils/plans_analytics_model.ts`:
- Around line 220-248: Rename the traffic series label associated with
uniqueVisitorOrganizations to “New visitor orgs” while leaving the underlying
counting logic unchanged. Update the corresponding display or returned label
wherever this series is defined or consumed near the analytics model.

In `@supabase/functions/_backend/utils/plans_analytics.ts`:
- Around line 373-395: Bound the organization set derived in the plans analytics
flow before issuing PostHog requests or calling loadPlansBillingHistories; when
it exceeds the explicit maximum, return emptyPlansAnalyticsResponse with
posthogFailureReason set to 'too_large'. Replace the serial await loop around
queryPosthogHogql and buildBillingTransitionsQuery with bounded-concurrency
batch execution, while preserving existing failure handling, row limits, and
transitionRows aggregation.

In `@supabase/functions/_backend/utils/plans_billing_history.ts`:
- Around line 407-446: Update the carry_in lateral lookup in the RevenueRow
query to select the latest daily_revenue_metrics row directly for each relevant
customer before the start date, ordering by date_id descending and limiting to
one row. Remove its dependency on processed_stripe_events.date_id while
preserving the existing carry-in columns and in_range behavior.

In `@supabase/functions/_backend/utils/posthog_read.ts`:
- Around line 25-26: Update the host and project resolution in the PostHog read
configuration to use the environment-specific configuration helper via
getRightKey() instead of the hard-coded EU host and project defaults. Preserve
the existing trimming and trailing-slash normalization while ensuring unset
values resolve from the current environment configuration.
- Around line 40-48: Update the PostHog response handling around response.json()
and row materialization to enforce a safe memory bound before allocating the
complete results array and mapped row objects. Prefer bounded response parsing,
or reduce the query limit to a Worker-safe maximum, while preserving
column-to-row mapping for accepted results.

In `@tests/admin-plans-analytics-dashboard.unit.test.ts`:
- Around line 202-298: Replace raw .vue source-text assertions in
tests/admin-plans-analytics-dashboard.unit.test.ts:202-298 with
mounted-component or pure-helper behavior assertions, preserving contracts for
the admin guard, fetchStats('plans_analytics'), and parsePlansAnalyticsResponse;
replace exact message-copy checks with required-key presence checks and remove
styling assertions for class="space-y-6" and lg:grid-cols-2. In
tests/admin-stacked-bar-chart.unit.test.ts:51-58, assert rendered Chart.js
dataset options for accessibleBorders enabled and disabled rather than matching
implementation source strings.
- Around line 63-67: Remove the two redundant toMatchTypeOf assertions from the
keeps the frontend response DTO identical to the backend wire contract test,
retaining only the toEqualTypeOf assertion to enforce exact type equality.

In `@tests/admin-stacked-bar-chart.unit.test.ts`:
- Around line 51-58: Update the test in the accessibleBorders assertion to match
the accessibleBorders prop block, including its default value, rather than
checking the bare “default: false” substring; keep the existing source-content
assertions for the contrasting boundary configuration.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ddddffd5-82f7-4081-b120-2baff2b23383

📥 Commits

Reviewing files that changed from the base of the PR and between e745fdd and 877312a.

📒 Files selected for processing (22)
  • docs/admin/plans-checkout-completion.md
  • docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
  • docs/superpowers/specs/2026-08-10-plans-analytics-dashboard-design.md
  • messages/en.json
  • src/components/admin/AdminStackedBarChart.vue
  • src/constants/adminTabs.ts
  • src/pages/admin/dashboard/plans.vue
  • src/services/adminPlansAnalytics.ts
  • src/stores/adminDashboard.ts
  • supabase/functions/_backend/private/admin_stats.ts
  • supabase/functions/_backend/utils/builder_analytics.ts
  • supabase/functions/_backend/utils/plans_analytics.ts
  • supabase/functions/_backend/utils/plans_analytics_model.ts
  • supabase/functions/_backend/utils/plans_billing_history.ts
  • supabase/functions/_backend/utils/posthog_read.ts
  • tests/admin-plans-analytics-dashboard.unit.test.ts
  • tests/admin-stacked-bar-chart.unit.test.ts
  • tests/admin-stats.unit.test.ts
  • tests/plans-analytics-model.unit.test.ts
  • tests/plans-analytics-orchestration.unit.test.ts
  • tests/plans-billing-history.unit.test.ts
  • tests/posthog-read.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md Outdated
Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
Comment thread supabase/functions/_backend/utils/posthog_read.ts Outdated
Comment thread supabase/functions/_backend/utils/posthog_read.ts Outdated
Comment thread tests/admin-plans-analytics-dashboard.unit.test.ts
Comment thread tests/admin-plans-analytics-dashboard.unit.test.ts
Comment thread tests/admin-stacked-bar-chart.unit.test.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md Outdated
Comment thread supabase/functions/_backend/utils/plans_analytics.ts
Comment thread supabase/functions/_backend/utils/posthog_read.ts Outdated
Comment thread supabase/functions/_backend/utils/plans_analytics.ts
Comment thread supabase/functions/_backend/utils/plans_billing_history.ts Outdated
Comment thread docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md Outdated
Comment thread tests/admin-stacked-bar-chart.unit.test.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: medium. Left a non-blocking comment; not approving because this exceeds the low-risk auto-approval threshold (large admin Plans analytics change with billing-history reconstruction and private admin API surface). Cursor Bugbot was not present after the initial check poll, so that signal was skipped; human review is needed and riderx is already assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@messages/en.json`:
- Line 2012: Update messages/en.json:2012-2012 to use timeout-specific retry
guidance instead of claiming the selected range is too large, and update the
expected localized timeout message in
tests/admin-plans-analytics-dashboard.unit.test.ts:56-56 to match.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84b6d1cf-f7d1-4ee9-988c-993c9cbd17b3

📥 Commits

Reviewing files that changed from the base of the PR and between 3d67ab1 and 98fc5e3.

📒 Files selected for processing (17)
  • docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md
  • docs/superpowers/specs/2026-08-10-plans-analytics-dashboard-design.md
  • messages/en.json
  • src/components/admin/AdminStackedBarChart.vue
  • src/components/admin/adminStackedBarChart.ts
  • src/pages/admin/dashboard/plans.vue
  • src/services/adminPlansAnalytics.ts
  • supabase/functions/_backend/utils/builder_analytics.ts
  • supabase/functions/_backend/utils/plans_analytics.ts
  • supabase/functions/_backend/utils/plans_billing_history.ts
  • supabase/functions/_backend/utils/posthog_read.ts
  • tests/admin-plans-analytics-dashboard.unit.test.ts
  • tests/admin-stacked-bar-chart.unit.test.ts
  • tests/plans-analytics-model.unit.test.ts
  • tests/plans-analytics-orchestration.unit.test.ts
  • tests/plans-billing-history.unit.test.ts
  • tests/posthog-read.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread messages/en.json Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk: medium. Left a non-blocking comment; not approving because this exceeds the low-risk auto-approval threshold (large admin Plans analytics change with billing-history reconstruction and private admin API surface). Cursor Bugbot was not present after the initial check poll, so that signal was skipped; human review is needed and riderx is already assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@sonarqubecloud

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 17 files (changes from recent commits).

Confidence score: 4/5

  • In tests/admin-plans-analytics-dashboard.unit.test.ts, loosening toMatchObject(requiredMessages) to a presence/type check means most message literals are no longer value-verified, which can let wording/key regressions slip through unnoticed — restore exact-value assertions (or add explicit per-key checks) for the full required message set.
  • In docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md, the added 08-02 12:00 fixture implies three opens on day 2 while totalOpens still expects [1, 2], so readers reusing this example can inherit a failing or misleading test — update the expectation to match the fixture (e.g., [1, 3]).
  • In tests/posthog-read.unit.test.ts, swapped it.each case labels don’t change behavior but can mislead future debugging and maintenance by describing the wrong override scenario — align the case names with their actual host/project overrides.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/admin-plans-analytics-dashboard.unit.test.ts">

<violation number="1" location="tests/admin-plans-analytics-dashboard.unit.test.ts:262">
P2: Replacing the toMatchObject(requiredMessages) assertion with a presence + string-type loop drops exact-value verification for every message except the timeout one. The ~30 other string literals left in requiredMessages are no longer compared against en.json, so a typo or wording change in any other message drifts through CI undetected while the constants still look like they're guarding the copy.</violation>
</file>

<file name="tests/posthog-read.unit.test.ts">

<violation number="1" location="tests/posthog-read.unit.test.ts:52">
P3: The it.each case labels are swapped relative to their overrides: "host override without project" blanks POSTHOG_READ_PROJECT_ID (keeps the host), and "project override without host" blanks POSTHOG_READ_HOST (keeps the project). Both still exercise the mismatch path so tests pass, but the generated test names read backwards and will mislead anyone debugging a failure.</violation>
</file>

<file name="docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md">

<violation number="1" location="docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md:416">
P3: The new 08-02 12:00 fixture event adds a third logical opening on day 2, but the `totalOpens` assertion still expects [1, 2]; it should be [1, 3]. Followers copying this fixture will get a failing/incorrect test, so update the expected array to match the added event.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

expect(completionDoc).toContain('pending until the agreed observation window')
expect(completionDoc).toContain('separate approved design')
const messages = JSON.parse(messagesText) as Record<string, unknown>
for (const key of Object.keys(requiredMessages)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Replacing the toMatchObject(requiredMessages) assertion with a presence + string-type loop drops exact-value verification for every message except the timeout one. The ~30 other string literals left in requiredMessages are no longer compared against en.json, so a typo or wording change in any other message drifts through CI undetected while the constants still look like they're guarding the copy.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/admin-plans-analytics-dashboard.unit.test.ts, line 262:

<comment>Replacing the toMatchObject(requiredMessages) assertion with a presence + string-type loop drops exact-value verification for every message except the timeout one. The ~30 other string literals left in requiredMessages are no longer compared against en.json, so a typo or wording change in any other message drifts through CI undetected while the constants still look like they're guarding the copy.</comment>

<file context>
@@ -215,7 +258,13 @@ describe('admin Plans analytics dashboard', () => {
     expect(completionDoc).toContain('separate approved design')
-    expect(JSON.parse(messagesText)).toMatchObject(requiredMessages)
+    const messages = JSON.parse(messagesText) as Record<string, unknown>
+    for (const key of Object.keys(requiredMessages)) {
+      expect(messages).toHaveProperty(key)
+      expect(messages[key]).toEqual(expect.any(String))
</file context>

Comment on lines +52 to +53
['host override without project', posthogEnv({ POSTHOG_READ_PROJECT_ID: '' })],
['project override without host', posthogEnv({ POSTHOG_READ_HOST: '' })],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The it.each case labels are swapped relative to their overrides: "host override without project" blanks POSTHOG_READ_PROJECT_ID (keeps the host), and "project override without host" blanks POSTHOG_READ_HOST (keeps the project). Both still exercise the mismatch path so tests pass, but the generated test names read backwards and will mislead anyone debugging a failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/posthog-read.unit.test.ts, line 52:

<comment>The it.each case labels are swapped relative to their overrides: "host override without project" blanks POSTHOG_READ_PROJECT_ID (keeps the host), and "project override without host" blanks POSTHOG_READ_HOST (keeps the project). Both still exercise the mismatch path so tests pass, but the generated test names read backwards and will mislead anyone debugging a failure.</comment>

<file context>
@@ -36,14 +47,51 @@ describe('postHog read transport', () => {
 
+  it.each([
+    ['key', posthogEnv({ POSTHOG_READ_KEY: '' })],
+    ['host override without project', posthogEnv({ POSTHOG_READ_PROJECT_ID: '' })],
+    ['project override without host', posthogEnv({ POSTHOG_READ_HOST: '' })],
+  ])('does not fetch when the %s part of the read configuration is missing', async (_missing, environment) => {
</file context>
Suggested change
['host override without project', posthogEnv({ POSTHOG_READ_PROJECT_ID: '' })],
['project override without host', posthogEnv({ POSTHOG_READ_HOST: '' })],
['host override without project', posthogEnv({ POSTHOG_READ_HOST: '' })],
['project override without host', posthogEnv({ POSTHOG_READ_PROJECT_ID: '' })],

const openings = buildLogicalPlansOpenings([
event({ timestampMs: ms('2026-08-01T08:00:00Z'), orgId: 'org-a', page: 'plans', path: '' }),
event({ timestampMs: ms('2026-08-02T08:00:00Z'), orgId: 'org-a', page: 'plans', path: '' }),
event({ timestampMs: ms('2026-08-02T12:00:00Z'), orgId: 'org-a', page: 'plans', path: '' }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new 08-02 12:00 fixture event adds a third logical opening on day 2, but the totalOpens assertion still expects [1, 2]; it should be [1, 3]. Followers copying this fixture will get a failing/incorrect test, so update the expected array to match the added event.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/superpowers/plans/2026-08-10-plans-analytics-dashboard.md, line 416:

<comment>The new 08-02 12:00 fixture event adds a third logical opening on day 2, but the `totalOpens` assertion still expects [1, 2]; it should be [1, 3]. Followers copying this fixture will get a failing/incorrect test, so update the expected array to match the added event.</comment>

<file context>
@@ -403,23 +413,29 @@ describe('Plans analytics model', () => {
     const openings = buildLogicalPlansOpenings([
       event({ timestampMs: ms('2026-08-01T08:00:00Z'), orgId: 'org-a', page: 'plans', path: '' }),
       event({ timestampMs: ms('2026-08-02T08:00:00Z'), orgId: 'org-a', page: 'plans', path: '' }),
+      event({ timestampMs: ms('2026-08-02T12:00:00Z'), orgId: 'org-a', page: 'plans', path: '' }),
       event({ timestampMs: ms('2026-08-02T09:00:00Z'), orgId: 'org-b', page: 'plans', path: '' }),
     ], ms('2026-08-01T00:00:00Z'), ms('2026-08-03T00:00:00Z'), 30)
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant