Skip to content

spec(experiments): CAEM-backed experiment goal result queries - #37287

Draft
freddyDOTCMS wants to merge 15 commits into
mainfrom
issue-37227-add-caem-backed-experiment-result-query-classes-with-legacy-cubejs-fallback-switch
Draft

spec(experiments): CAEM-backed experiment goal result queries#37287
freddyDOTCMS wants to merge 15 commits into
mainfrom
issue-37227-add-caem-backed-experiment-result-query-classes-with-legacy-cubejs-fallback-switch

Conversation

@freddyDOTCMS

Copy link
Copy Markdown
Member

Summary

  • Adds spec.md for issue Add CAEM-backed experiment result query classes with legacy CubeJS fallback switch #37227 — CAEM-backed result query implementations (bounce rate, exit rate, reach-target, URL-param) that call the CAEM analytics API instead of CubeJS
  • Both implementation paths coexist; a runtime configuration switch (DOT_CAEM_ENABLED) selects between them with CubeJS as the safe default
  • Spec covers 5 user stories (3× P1, 2× P2), 17 FRs, 7 success criteria, and Legacy Considerations

Closes #37227
Depends on: #37223 (CAEM sessions endpoint), #37224 (CAEM sessions behavior endpoint)

Review checklist

  • User stories are independently testable and correctly prioritized
  • Functional requirements are testable and unambiguous
  • Legacy Considerations accurately describe the backward-compatibility guarantee
  • Assumptions are valid given current CAEM pipeline state

Test plan

  • This PR carries spec.md only — no implementation code
  • Reviewer approves the spec before /speckit-plan runs on the feature branch

🤖 Generated with Claude Code

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @freddyDOTCMS's task in 1m 43s —— View job


Spec Review — CAEM-backed experiment goal result queries

  • Read spec.md
  • Verified referenced classes, interface, factory, and goal types against the codebase
  • Posted review

This PR carries spec.md only, so there is no executable code to review. I verified the spec's claims against the real code it will touch. The spec is well-structured and mostly accurate — the class names, package (com.dotcms.experiments.business.result), goal types (BOUNCE_RATE, EXIT_RATE, REACH_PAGE, URL_PARAMETER, CLICK_ON_ELEMENT), and the fact that URL_PARAMETER currently reuses ReachTargetAfterExperimentPageResultQuery (ExperimentResultsQueryFactory.java:73) are all correct. Two design assumptions in the spec are in tension with how the current code is actually wired — worth resolving before /speckit-plan so the plan targets the right seam.

New Issues

  • 🟡 Medium: specs/37227-caem-experiment-goal-queries/spec.md:136-137 (Key Entities) — The switch cannot live in ExperimentResultsQueryFactory / MetricExperimentResultsQuery as described. That interface returns a CubeJSQuery (MetricExperimentResultsQuery.java:18), and the factory's create() / createWithDayGranularity() produce a CubeJSQuery that ExperimentsAPIImpl.getSummary()/getTotalSessions() send via cubeClient.send(...), yielding a CubeJSResultSet that flows into the private getResults(CubeJSResultSet, …) and then Bayesian (ExperimentsAPIImpl.java:1430-1441, 1311). A CAEM-backed class making HTTP calls to CAEM cannot naturally "implement this same interface" (it has no CubeJSQuery to return), and even if it did, the downstream pipeline consumes a CubeJSResultSet, not a neutral result model. The real dispatch seam is one level up — getSummary/getTotalSessions in ExperimentsAPIImpl — not the query factory. Recommend the spec either introduce a new abstraction (e.g. a ResultQueryExecutor returning the neutral ExperimentResults/VariantResults model) or explicitly name ExperimentsAPIImpl as the switch point. As written, FR-013 and the "call sites unchanged" claim in Legacy Considerations (spec.md:161) understate the scope.

  • 🟡 Medium: specs/37227-caem-experiment-goal-queries/spec.md:123,151 (FR-008 / SC-003 / US5 scenario 3) — "Toggle at runtime without a restart" conflicts with the factory's cached dispatch map. ExperimentResultsQueryFactory holds experimentResultQueryHelpers in a Lazy.of(() -> createHelpersMap()) (ExperimentResultsQueryFactory.java:64-65) that is computed once and never recomputed. If the CAEM/CubeJS selection is resolved at map-construction time, flipping DOT_CAEM_ENABLED will not take effect until a restart, violating SC-003 and US5 scenario 3. The plan must read the config on each evaluation (per-call dispatch), not at initialization. Worth calling out in the spec so this isn't discovered late.

  • 🟡 Medium: specs/37227-caem-experiment-goal-queries/spec.md:35,106,108,132"Surface the failure" is asserted repeatedly but never defined. Edge cases and FR-017 require CAEM errors (auth failure, non-2xx, malformed body, unreachable endpoint) to be "surfaced rather than silently swallowed," but the spec never states how — thrown exception vs. error field on the result vs. logged-and-empty. The current CubeJS path returns a populated ExperimentResults; consumers (admin UI, results API, Bayesian calc at ExperimentsAPIImpl.java:1418) have no defined behavior for a hard failure. Recommend one FR pinning the observable failure contract (exception type / HTTP status returned to the API caller) so SC-004's "error surfacing" is testable rather than subjective.

Notes (non-blocking)

Overall: solid spec, no blocking issues. The two design-seam findings above are the main things to reconcile before planning, since they change where the code lands.
· issue-37227-add-caem-backed-experiment-result-query-classes-with-legacy-cubejs-fallback-switch

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Add CAEM-backed experiment result query classes with legacy CubeJS fallback switch

1 participant