Skip to content

refactor(experiments): Move pausing/resuming experiments to API#52051

Merged
mp-hog merged 4 commits intomasterfrom
experiments/move-pause-resumse-experiments-to-API
Mar 24, 2026
Merged

refactor(experiments): Move pausing/resuming experiments to API#52051
mp-hog merged 4 commits intomasterfrom
experiments/move-pause-resumse-experiments-to-API

Conversation

@mp-hog
Copy link
Contributor

@mp-hog mp-hog commented Mar 24, 2026

Problem

This is one step in the bigger initiative to reduce logic from the client and add it to the API.

In this PR: Move pausing/resuming to API

Changes

  • New API endpoints
  • New actions in service object
  • Validation moved
  • Tracking moved

As a nice UX benefit, the actions feel snappier as the frontend state update got simpler (no await for two separate state updates)

How did you test this code?

  • Test cases for feature flag update, frontend state update and error handling
  • Manually: Pause and resume experiment

Publish to changelog?

No

@github-actions
Copy link
Contributor

github-actions bot commented Mar 24, 2026

Size Change: -1.92 kB (0%)

Total Size: 110 MB

ℹ️ View Unchanged
Filename Size Change
frontend/dist/368Hedgehogs 5.27 kB 0 B
frontend/dist/abap 14.2 kB 0 B
frontend/dist/Action 20.5 kB 0 B
frontend/dist/Actions 1.03 kB 0 B
frontend/dist/AdvancedActivityLogsScene 34 kB 0 B
frontend/dist/AgenticAuthorize 5.27 kB 0 B
frontend/dist/apex 3.96 kB 0 B
frontend/dist/ApprovalDetail 16.2 kB 0 B
frontend/dist/array.full.es5.js 324 kB 0 B
frontend/dist/array.full.js 420 kB 0 B
frontend/dist/array.js 176 kB 0 B
frontend/dist/AsyncMigrations 13.2 kB 0 B
frontend/dist/AuthorizationStatus 717 B 0 B
frontend/dist/azcli 852 B 0 B
frontend/dist/bat 1.85 kB 0 B
frontend/dist/BatchExportScene 50.6 kB 0 B
frontend/dist/bicep 2.56 kB 0 B
frontend/dist/Billing 493 B 0 B
frontend/dist/BillingSection 20.7 kB 0 B
frontend/dist/BoxPlot 5.01 kB 0 B
frontend/dist/browserAll-0QZMN1W2 37.4 kB 0 B
frontend/dist/ButtonPrimitives 562 B 0 B
frontend/dist/CalendarHeatMap 4.82 kB 0 B
frontend/dist/cameligo 2.2 kB 0 B
frontend/dist/changeRequestsLogic 544 B 0 B
frontend/dist/CLIAuthorize 11.3 kB 0 B
frontend/dist/CLILive 3.99 kB 0 B
frontend/dist/clojure 9.65 kB 0 B
frontend/dist/coffee 3.6 kB 0 B
frontend/dist/Cohort 23.3 kB 0 B
frontend/dist/CohortCalculationHistory 6.24 kB 0 B
frontend/dist/Cohorts 9.41 kB 0 B
frontend/dist/ConfirmOrganization 4.5 kB 0 B
frontend/dist/conversations.js 63.8 kB 0 B
frontend/dist/Coupons 731 B 0 B
frontend/dist/cpp 5.31 kB 0 B
frontend/dist/Create 840 B +174 B (+26.13%) 🚨
frontend/dist/crisp-chat-integration.js 1.88 kB 0 B
frontend/dist/csharp 4.53 kB 0 B
frontend/dist/csp 1.43 kB 0 B
frontend/dist/css 4.51 kB 0 B
frontend/dist/cssMode 4.18 kB 0 B
frontend/dist/CustomCssScene 3.56 kB 0 B
frontend/dist/CustomerAnalyticsConfigurationScene 2 kB 0 B
frontend/dist/CustomerAnalyticsScene 26.4 kB 0 B
frontend/dist/CustomerJourneyBuilderScene 1.61 kB 0 B
frontend/dist/CustomerJourneyTemplatesScene 7.42 kB 0 B
frontend/dist/customizations.full.js 17.8 kB 0 B
frontend/dist/CyclotronJobInputAssignee 1.33 kB 0 B
frontend/dist/CyclotronJobInputTicketTags 717 B 0 B
frontend/dist/cypher 3.4 kB 0 B
frontend/dist/dart 4.26 kB 0 B
frontend/dist/Dashboard 1 kB 0 B
frontend/dist/Dashboards 14.7 kB 0 B
frontend/dist/DataManagementScene 646 B 0 B
frontend/dist/DataPipelinesNewScene 2.33 kB 0 B
frontend/dist/DataWarehouseScene 1.11 kB 0 B
frontend/dist/DataWarehouseSourceScene 634 B 0 B
frontend/dist/Deactivated 1.14 kB 0 B
frontend/dist/dead-clicks-autocapture.js 12.5 kB 0 B
frontend/dist/DeadLetterQueue 5.41 kB 0 B
frontend/dist/DebugScene 20.1 kB 0 B
frontend/dist/decompressionWorker 2.85 kB 0 B
frontend/dist/decompressionWorker.js 2.85 kB 0 B
frontend/dist/DefinitionEdit 7.13 kB 0 B
frontend/dist/DefinitionView 22.8 kB 0 B
frontend/dist/DestinationsScene 2.72 kB 0 B
frontend/dist/dist 575 B 0 B
frontend/dist/dockerfile 1.88 kB 0 B
frontend/dist/EarlyAccessFeature 685 B 0 B
frontend/dist/EarlyAccessFeatures 2.85 kB 0 B
frontend/dist/ecl 5.35 kB 0 B
frontend/dist/EditorScene 839 B 0 B
frontend/dist/elixir 10.3 kB 0 B
frontend/dist/EmailMFAVerify 2.99 kB 0 B
frontend/dist/EndpointScene 37.2 kB 0 B
frontend/dist/EndpointsScene 21 kB 0 B
frontend/dist/ErrorTrackingConfigurationScene 2.2 kB 0 B
frontend/dist/ErrorTrackingIssueFingerprintsScene 6.99 kB 0 B
frontend/dist/ErrorTrackingIssueScene 82.1 kB 0 B
frontend/dist/ErrorTrackingScene 13 kB 0 B
frontend/dist/EvaluationTemplates 575 B 0 B
frontend/dist/EventsScene 2.45 kB 0 B
frontend/dist/exception-autocapture.js 11.7 kB 0 B
frontend/dist/Experiment 261 kB 0 B
frontend/dist/Experiments 17.1 kB 0 B
frontend/dist/exporter 21 MB -422 B (0%)
frontend/dist/exporter.js 21 MB -422 B (0%)
frontend/dist/ExportsScene 3.88 kB 0 B
frontend/dist/FeatureFlag 102 kB 0 B
frontend/dist/FeatureFlags 572 B 0 B
frontend/dist/FeatureFlagTemplatesScene 7.05 kB 0 B
frontend/dist/FlappyHog 5.8 kB 0 B
frontend/dist/flow9 1.81 kB 0 B
frontend/dist/freemarker2 16.7 kB 0 B
frontend/dist/fsharp 2.99 kB 0 B
frontend/dist/go 2.66 kB 0 B
frontend/dist/graphql 2.27 kB 0 B
frontend/dist/Group 14.5 kB 0 B
frontend/dist/Groups 3.94 kB 0 B
frontend/dist/GroupsNew 7.36 kB 0 B
frontend/dist/handlebars 7.37 kB 0 B
frontend/dist/hcl 3.6 kB 0 B
frontend/dist/HealthScene 12 kB 0 B
frontend/dist/HeatmapNewScene 4.18 kB 0 B
frontend/dist/HeatmapRecordingScene 3.94 kB 0 B
frontend/dist/HeatmapScene 6.05 kB 0 B
frontend/dist/HeatmapsScene 3.89 kB 0 B
frontend/dist/HogFunctionScene 58.8 kB 0 B
frontend/dist/HogRepl 7.38 kB 0 B
frontend/dist/html 5.6 kB 0 B
frontend/dist/htmlMode 4.64 kB 0 B
frontend/dist/image-blob-reduce.esm 49.4 kB 0 B
frontend/dist/InboxScene 54.6 kB 0 B
frontend/dist/index 255 kB 0 B
frontend/dist/index.js 255 kB 0 B
frontend/dist/ini 1.11 kB 0 B
frontend/dist/InsightOptions 4.81 kB 0 B
frontend/dist/InsightScene 27.1 kB 0 B
frontend/dist/IntegrationsRedirect 744 B 0 B
frontend/dist/intercom-integration.js 1.93 kB 0 B
frontend/dist/InviteSignup 13.3 kB 0 B
frontend/dist/java 3.23 kB 0 B
frontend/dist/javascript 996 B 0 B
frontend/dist/jsonMode 13.9 kB 0 B
frontend/dist/julia 7.24 kB 0 B
frontend/dist/kotlin 3.41 kB 0 B
frontend/dist/lazy 151 kB 0 B
frontend/dist/LegacyPluginScene 21.2 kB 0 B
frontend/dist/less 3.9 kB 0 B
frontend/dist/lexon 2.45 kB 0 B
frontend/dist/lib 2.23 kB 0 B
frontend/dist/Link 468 B 0 B
frontend/dist/LinkScene 24.9 kB 0 B
frontend/dist/LinksScene 4.21 kB 0 B
frontend/dist/liquid 4.54 kB 0 B
frontend/dist/LiveDebugger 19.2 kB 0 B
frontend/dist/LiveEventsTable 4.46 kB 0 B
frontend/dist/LLMAnalyticsClusterScene 15.7 kB 0 B
frontend/dist/LLMAnalyticsClustersScene 43.1 kB 0 B
frontend/dist/LLMAnalyticsDatasetScene 19.7 kB 0 B
frontend/dist/LLMAnalyticsDatasetsScene 3.29 kB 0 B
frontend/dist/LLMAnalyticsEvaluation 40.5 kB 0 B
frontend/dist/LLMAnalyticsEvaluationsScene 29.3 kB 0 B
frontend/dist/LLMAnalyticsPlaygroundScene 36.4 kB 0 B
frontend/dist/LLMAnalyticsScene 113 kB 0 B
frontend/dist/LLMAnalyticsSessionScene 13.4 kB 0 B
frontend/dist/LLMAnalyticsTraceScene 127 kB 0 B
frontend/dist/LLMAnalyticsUsers 526 B 0 B
frontend/dist/LLMASessionFeedbackDisplay 4.85 kB 0 B
frontend/dist/LLMPromptScene 16.9 kB 0 B
frontend/dist/LLMPromptsScene 4.23 kB 0 B
frontend/dist/Login 8.51 kB 0 B
frontend/dist/Login2FA 4.22 kB 0 B
frontend/dist/logs.js 38.3 kB 0 B
frontend/dist/LogsScene 18.5 kB 0 B
frontend/dist/lua 2.13 kB 0 B
frontend/dist/m3 2.82 kB 0 B
frontend/dist/ManagedMigration 14 kB 0 B
frontend/dist/markdown 3.79 kB 0 B
frontend/dist/MarketingAnalyticsScene 24.7 kB 0 B
frontend/dist/MaterializedColumns 10.2 kB 0 B
frontend/dist/Max 767 B 0 B
frontend/dist/mdx 5.38 kB 0 B
frontend/dist/MessageTemplate 16.3 kB 0 B
frontend/dist/MetricsScene 844 B 0 B
frontend/dist/mips 2.59 kB 0 B
frontend/dist/ModelsScene 13.7 kB 0 B
frontend/dist/MonacoDiffEditor 403 B 0 B
frontend/dist/MoveToPostHogCloud 4.46 kB 0 B
frontend/dist/msdax 4.92 kB 0 B
frontend/dist/mysql 11.3 kB 0 B
frontend/dist/NavTabChat 4.55 kB 0 B
frontend/dist/NewSourceWizard 758 B 0 B
frontend/dist/NewTabScene 647 B 0 B
frontend/dist/NodeDetailScene 15.5 kB 0 B
frontend/dist/NotebookCanvasScene 3.04 kB 0 B
frontend/dist/NotebookPanel 5.07 kB 0 B
frontend/dist/NotebookScene 8.06 kB 0 B
frontend/dist/NotebooksScene 7.61 kB 0 B
frontend/dist/OAuthAuthorize 573 B 0 B
frontend/dist/objective-c 2.42 kB 0 B
frontend/dist/Onboarding 672 kB 0 B
frontend/dist/OnboardingCouponRedemption 1.2 kB 0 B
frontend/dist/pascal 3 kB 0 B
frontend/dist/pascaligo 2.01 kB 0 B
frontend/dist/passkeyLogic 484 B 0 B
frontend/dist/PasswordReset 4.33 kB 0 B
frontend/dist/PasswordResetComplete 2.95 kB 0 B
frontend/dist/perl 8.26 kB 0 B
frontend/dist/PersonScene 16 kB 0 B
frontend/dist/PersonsScene 4.75 kB 0 B
frontend/dist/pgsql 13.5 kB 0 B
frontend/dist/php 8.03 kB 0 B
frontend/dist/PipelineStatusScene 6.24 kB 0 B
frontend/dist/pla 1.69 kB 0 B
frontend/dist/posthog 251 kB 0 B
frontend/dist/postiats 7.86 kB 0 B
frontend/dist/powerquery 17 kB 0 B
frontend/dist/powershell 3.28 kB 0 B
frontend/dist/PreflightCheck 5.54 kB 0 B
frontend/dist/product-tours.js 115 kB 0 B
frontend/dist/ProductTour 274 kB 0 B
frontend/dist/ProductTours 4.72 kB 0 B
frontend/dist/ProjectHomepage 21.9 kB 0 B
frontend/dist/protobuf 9.05 kB 0 B
frontend/dist/pug 4.83 kB 0 B
frontend/dist/python 4.79 kB 0 B
frontend/dist/qsharp 3.2 kB 0 B
frontend/dist/r 3.14 kB 0 B
frontend/dist/razor 9.36 kB 0 B
frontend/dist/recorder-v2.js 111 kB 0 B
frontend/dist/recorder.js 111 kB 0 B
frontend/dist/redis 3.56 kB 0 B
frontend/dist/redshift 11.8 kB 0 B
frontend/dist/RegionMap 29.5 kB 0 B
frontend/dist/render-query 20.8 MB -422 B (0%)
frontend/dist/render-query.js 20.8 MB -422 B (0%)
frontend/dist/ResourceTransfer 9.16 kB 0 B
frontend/dist/restructuredtext 3.91 kB 0 B
frontend/dist/RevenueAnalyticsScene 25.6 kB 0 B
frontend/dist/ruby 8.51 kB 0 B
frontend/dist/rust 4.17 kB 0 B
frontend/dist/SavedInsights 664 B 0 B
frontend/dist/sb 1.83 kB 0 B
frontend/dist/scala 7.33 kB 0 B
frontend/dist/scheme 1.77 kB 0 B
frontend/dist/scss 6.41 kB 0 B
frontend/dist/SdkDoctorScene 11 kB 0 B
frontend/dist/SessionAttributionExplorerScene 6.62 kB 0 B
frontend/dist/SessionGroupSummariesTable 4.64 kB 0 B
frontend/dist/SessionGroupSummaryScene 17.1 kB 0 B
frontend/dist/SessionProfileScene 15.9 kB 0 B
frontend/dist/SessionRecordingDetail 1.74 kB 0 B
frontend/dist/SessionRecordingFilePlaybackScene 4.48 kB 0 B
frontend/dist/SessionRecordings 742 B 0 B
frontend/dist/SessionRecordingsKiosk 8.87 kB 0 B
frontend/dist/SessionRecordingsPlaylistScene 4.08 kB 0 B
frontend/dist/SessionRecordingsSettingsScene 1.91 kB 0 B
frontend/dist/SessionsScene 3.88 kB 0 B
frontend/dist/SettingsScene 3 kB 0 B
frontend/dist/SharedMetric 15.7 kB 0 B
frontend/dist/SharedMetrics 515 B 0 B
frontend/dist/shell 3.08 kB 0 B
frontend/dist/SignupContainer 22.8 kB 0 B
frontend/dist/Site 1.2 kB 0 B
frontend/dist/solidity 18.6 kB 0 B
frontend/dist/sophia 2.77 kB 0 B
frontend/dist/SourcesScene 5.98 kB 0 B
frontend/dist/sourceWizardLogic 662 B 0 B
frontend/dist/sparql 2.56 kB 0 B
frontend/dist/sql 10.3 kB 0 B
frontend/dist/SqlVariableEditScene 7.26 kB 0 B
frontend/dist/st 7.41 kB 0 B
frontend/dist/StartupProgram 21.2 kB 0 B
frontend/dist/SupportSettingsScene 36.2 kB 0 B
frontend/dist/SupportTicketScene 22.8 kB 0 B
frontend/dist/SupportTicketsScene 733 B 0 B
frontend/dist/Survey 746 B 0 B
frontend/dist/SurveyFormBuilder 1.55 kB 0 B
frontend/dist/Surveys 18.2 kB 0 B
frontend/dist/surveys.js 89.8 kB 0 B
frontend/dist/SurveyWizard 56.2 kB 0 B
frontend/dist/swift 5.28 kB 0 B
frontend/dist/SystemStatus 16.9 kB 0 B
frontend/dist/systemverilog 7.62 kB 0 B
frontend/dist/TaskDetailScene 19.1 kB 0 B
frontend/dist/TaskTracker 16.4 kB 0 B
frontend/dist/tcl 3.57 kB 0 B
frontend/dist/toolbar 9.84 MB 0 B
frontend/dist/toolbar.js 9.84 MB -406 B (0%)
frontend/dist/ToolbarLaunch 2.53 kB 0 B
frontend/dist/tracing-headers.js 1.74 kB 0 B
frontend/dist/TracingScene 14.5 kB 0 B
frontend/dist/TransformationsScene 1.97 kB 0 B
frontend/dist/tsMode 24 kB 0 B
frontend/dist/twig 5.98 kB 0 B
frontend/dist/TwoFactorReset 4 kB 0 B
frontend/dist/typescript 240 B 0 B
frontend/dist/typespec 2.83 kB 0 B
frontend/dist/Unsubscribe 1.63 kB 0 B
frontend/dist/UserInterview 4.55 kB 0 B
frontend/dist/UserInterviews 2.03 kB 0 B
frontend/dist/vb 5.8 kB 0 B
frontend/dist/VercelConnect 4.03 kB 0 B
frontend/dist/VercelLinkError 1.92 kB 0 B
frontend/dist/VerifyEmail 4.49 kB 0 B
frontend/dist/vimMode 211 kB 0 B
frontend/dist/VisualReviewRunScene 18.7 kB 0 B
frontend/dist/VisualReviewRunsScene 6.14 kB 0 B
frontend/dist/VisualReviewSettingsScene 9.97 kB 0 B
frontend/dist/web-vitals.js 6.39 kB 0 B
frontend/dist/WebAnalyticsScene 5.72 kB 0 B
frontend/dist/WebGLRenderer-DYjOwNoG 60.3 kB 0 B
frontend/dist/WebGPURenderer-B_wkl_Ja 36.3 kB 0 B
frontend/dist/WebScriptsScene 2.58 kB 0 B
frontend/dist/webworkerAll-puPV1rBA 330 B 0 B
frontend/dist/wgsl 7.35 kB 0 B
frontend/dist/Wizard 4.47 kB 0 B
frontend/dist/WorkflowScene 101 kB 0 B
frontend/dist/WorkflowsScene 47.5 kB 0 B
frontend/dist/WorldMap 4.75 kB 0 B
frontend/dist/xml 2.98 kB 0 B
frontend/dist/yaml 4.61 kB 0 B

compressed-size-action

@mp-hog mp-hog marked this pull request as ready for review March 24, 2026 11:21
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 24, 2026

Comments Outside Diff (2)

  1. products/experiments/backend/experiment_service.py, line 565-597 (link)

    P2 DRY violation: identical reporting helpers

    _report_experiment_paused, _report_experiment_resumed, and _report_experiment_archived (existing) are structurally identical — they differ only in the event-name string. This violates the project's OnceAndOnlyOnce rule. A single private helper would eliminate the duplication:

    def _report_experiment_action(
        self,
        event: str,
        experiment: Experiment,
        *,
        request: Any | None = None,
    ) -> None:
        if request is None:
            return
        report_user_action(
            self.user,
            event,
            experiment.get_analytics_metadata(),
            team=experiment.team,
            request=request,
        )

    Then callers become self._report_experiment_action("experiment paused", experiment, request=request), etc.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: products/experiments/backend/experiment_service.py
    Line: 565-597
    
    Comment:
    **DRY violation: identical reporting helpers**
    
    `_report_experiment_paused`, `_report_experiment_resumed`, and `_report_experiment_archived` (existing) are structurally identical — they differ only in the event-name string. This violates the project's OnceAndOnlyOnce rule. A single private helper would eliminate the duplication:
    
    ```python
    def _report_experiment_action(
        self,
        event: str,
        experiment: Experiment,
        *,
        request: Any | None = None,
    ) -> None:
        if request is None:
            return
        report_user_action(
            self.user,
            event,
            experiment.get_analytics_metadata(),
            team=experiment.team,
            request=request,
        )
    ```
    
    Then callers become `self._report_experiment_action("experiment paused", experiment, request=request)`, etc.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

  2. ee/clickhouse/views/test/test_clickhouse_experiments.py, line 145-179 (link)

    P2 Non-parameterised tests and missing resume coverage

    Two issues here:

    1. test_pause_draft_experiment_returns_400 and test_pause_ended_experiment_returns_400 are separate test methods that test the same code path with different states. They should be collapsed into a single @parameterized.expand test, consistent with test_pause_experiment_wrong_state_raises in test_experiment_service.py.

    2. There is no corresponding API-level test for resuming a draft or ended experiment (i.e., a test_resume_draft_experiment_returns_400 / test_resume_ended_experiment_returns_400). The service tests cover these branches, but having symmetrical API-level coverage is worthwhile for integration confidence.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: ee/clickhouse/views/test/test_clickhouse_experiments.py
    Line: 145-179
    
    Comment:
    **Non-parameterised tests and missing `resume` coverage**
    
    Two issues here:
    
    1. `test_pause_draft_experiment_returns_400` and `test_pause_ended_experiment_returns_400` are separate test methods that test the same code path with different states. They should be collapsed into a single `@parameterized.expand` test, consistent with `test_pause_experiment_wrong_state_raises` in `test_experiment_service.py`.
    
    2. There is no corresponding API-level test for resuming a **draft** or **ended** experiment (i.e., a `test_resume_draft_experiment_returns_400` / `test_resume_ended_experiment_returns_400`). The service tests cover these branches, but having symmetrical API-level coverage is worthwhile for integration confidence.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
This is a comment left during a code review.
Path: products/experiments/backend/experiment_service.py
Line: 565-597

Comment:
**DRY violation: identical reporting helpers**

`_report_experiment_paused`, `_report_experiment_resumed`, and `_report_experiment_archived` (existing) are structurally identical — they differ only in the event-name string. This violates the project's OnceAndOnlyOnce rule. A single private helper would eliminate the duplication:

```python
def _report_experiment_action(
    self,
    event: str,
    experiment: Experiment,
    *,
    request: Any | None = None,
) -> None:
    if request is None:
        return
    report_user_action(
        self.user,
        event,
        experiment.get_analytics_metadata(),
        team=experiment.team,
        request=request,
    )
```

Then callers become `self._report_experiment_action("experiment paused", experiment, request=request)`, etc.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: ee/clickhouse/views/test/test_clickhouse_experiments.py
Line: 145-179

Comment:
**Non-parameterised tests and missing `resume` coverage**

Two issues here:

1. `test_pause_draft_experiment_returns_400` and `test_pause_ended_experiment_returns_400` are separate test methods that test the same code path with different states. They should be collapsed into a single `@parameterized.expand` test, consistent with `test_pause_experiment_wrong_state_raises` in `test_experiment_service.py`.

2. There is no corresponding API-level test for resuming a **draft** or **ended** experiment (i.e., a `test_resume_draft_experiment_returns_400` / `test_resume_ended_experiment_returns_400`). The service tests cover these branches, but having symmetrical API-level coverage is worthwhile for integration confidence.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: products/experiments/backend/models/experiment.py
Line: 152-154

Comment:
**`has_ended` excludes the ARCHIVED status**

The property returns `True` only for `Status.STOPPED`. An archived experiment (one that has `archived=True`) could conceivably have a status other than STOPPED in edge cases or future evolution. More importantly, `archive_experiment` now relies on `has_ended` instead of the previous `end_date` check — an experiment is archiveable only when it has a non-null `end_date`, but `has_ended` checks a computed status. If the `status` field is cached/set to a value other than STOPPED while `end_date` is non-null (e.g. `ARCHIVED` as a future status variant), `archive_experiment` would incorrectly raise `"Experiment must be ended before it can be archived."`.

The property name could also be more precise — consider `is_stopped` to match the `Status.STOPPED` value and be unambiguous.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "chore: update OpenAPI generated types" | Re-trigger Greptile

Copy link
Contributor

@andehen andehen left a comment

Choose a reason for hiding this comment

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

Nice! Code looks good 👍

@mp-hog mp-hog enabled auto-merge (squash) March 24, 2026 14:36
@mp-hog mp-hog merged commit bf56308 into master Mar 24, 2026
216 checks passed
@mp-hog mp-hog deleted the experiments/move-pause-resumse-experiments-to-API branch March 24, 2026 14:59
@mp-hog mp-hog added the skip-inkeep-docs Use this label to skip an Inkeep docs PR in posthog.com label Mar 24, 2026
matt-metivier pushed a commit that referenced this pull request Mar 25, 2026
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-inkeep-docs Use this label to skip an Inkeep docs PR in posthog.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants