Skip to content

feat(flags): creation intents#49586

Merged
ordehi merged 20 commits intomasterfrom
feat/feature-flag-creation-intents
Mar 9, 2026
Merged

feat(flags): creation intents#49586
ordehi merged 20 commits intomasterfrom
feat/feature-flag-creation-intents

Conversation

@ordehi
Copy link
Contributor

@ordehi ordehi commented Mar 2, 2026

What this does

Adds evaluation intent declaration to the V2 feature flag creation flow. When creating a flag, users now declare how they intend to use it "Local evaluation" or "Prevent flicker" and the form surfaces contextual warnings scoped to that intent. Also adds unreachable condition detection as an always-on structural warning.

All work is behind FEATURE_FLAG_CREATION_INTENTS (requires FEATURE_FLAGS_V2).

Why this matters

Feature flags are deceptively easy to misconfigure. A user who wants local evaluation might add a static cohort (not sent to SDKs), use is_not_set (unknowable without the full property list), or enable experience continuity (requires server state). Today, the form shows nothing, the flag silently doesn't work as expected.

Warnings are only useful when they have context about what the user is trying to do. Generic "this cohort is static" messages get ignored. "This static cohort will force a server request, removing the speed and cost benefits of local evaluation" gets acted on.

This is the first piece of an intent-driven correctness system for flags. The pattern:

  1. Declare intent (this PR): user picks an evaluation intent at creation time
  2. Contextual warnings (this PR): form warns about configuration that conflicts with the declared intent
  3. Pre-save validation gates (follow-up): block saving flags with critical intent violations, with an explicit override
  4. Runtime guardrails (future): API-level validation that prevents drift after creation
  5. Product topology mapping (future): Detect and prefill product topology properties like tags for evaluation scopes

How this maps to the wider product topology

Feature flags sit at the intersection of multiple evaluation paths (client SDK, server SDK with local eval, bootstrapped first-page-load). Each path has different constraints on what's evaluable. Today, users discover these constraints through debugging in production. Intent declaration moves that discovery to creation time.

The intent system is designed to be extensible: new intents (e.g., "edge evaluation", "mobile offline") slot in by adding entries to INTENT_METADATA and check functions to the warning logic. The unreachableGroups detector is intent-agnostic and catches structural issues regardless. This will allow us to provide user-defined intents to further map PostHog to the user's product.

How did you test this code?

  • Wrote tests
  • Locally
CleanShot.2026-03-02.at.18.01.10-converted.mp4

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Publish to changelog?

No

@ordehi ordehi requested a review from a team March 2, 2026 22:55
@posthog-project-board-bot posthog-project-board-bot bot moved this to In Review in Feature Flags Mar 2, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Size Change: +35.2 kB (+0.03%)

Total Size: 112 MB

Filename Size Change
frontend/dist/exporter 21.2 MB +5.39 kB (+0.03%)
frontend/dist/exporter.js 21.2 MB +5.39 kB (+0.03%)
frontend/dist/FeatureFlagTemplatesScene 7.9 kB +2.83 kB (+55.83%) 🆘
frontend/dist/render-query 21 MB +5.39 kB (+0.03%)
frontend/dist/render-query.js 21 MB +5.39 kB (+0.03%)
frontend/dist/toolbar 9.82 MB +5.38 kB (+0.05%)
frontend/dist/toolbar.js 9.82 MB +5.38 kB (+0.05%)
ℹ️ View Unchanged
Filename Size
frontend/dist/1c 160 kB
frontend/dist/368Hedgehogs 6.12 kB
frontend/dist/abap 145 B
frontend/dist/abnf 145 B
frontend/dist/accesslog 1.04 kB
frontend/dist/Action 21 kB
frontend/dist/Actions 1.89 kB
frontend/dist/actionscript 153 B
frontend/dist/ada 144 B
frontend/dist/AdvancedActivityLogsScene 34.7 kB
frontend/dist/agda 145 B
frontend/dist/al 143 B
frontend/dist/angelscript 1.73 kB
frontend/dist/antlr4 147 B
frontend/dist/apache 1.05 kB
frontend/dist/apacheconf 151 B
frontend/dist/apex 179 B
frontend/dist/apl 144 B
frontend/dist/applescript 152 B
frontend/dist/ApprovalDetail 17.1 kB
frontend/dist/aql 144 B
frontend/dist/arcade 2.94 kB
frontend/dist/arduino 8.88 kB
frontend/dist/arff 145 B
frontend/dist/armasm 3.27 kB
frontend/dist/array.full.es5.js 330 kB
frontend/dist/array.full.js 428 kB
frontend/dist/array.js 183 kB
frontend/dist/asciidoc 149 B
frontend/dist/asm6502 148 B
frontend/dist/asmatmel 149 B
frontend/dist/aspectj 2.69 kB
frontend/dist/aspnet 181 B
frontend/dist/AsyncMigrations 14 kB
frontend/dist/AuthorizationStatus 1.57 kB
frontend/dist/autohotkey 1.04 kB
frontend/dist/autoit 6.74 kB
frontend/dist/avisynth 149 B
frontend/dist/avrasm 2.1 kB
frontend/dist/avro-idl 149 B
frontend/dist/awk 804 B
frontend/dist/axapta 1.76 kB
frontend/dist/azcli 852 B
frontend/dist/bash 2.18 kB
frontend/dist/basic 146 B
frontend/dist/bat 1.85 kB
frontend/dist/batch 146 B
frontend/dist/BatchExportScene 50.7 kB
frontend/dist/bbcode 147 B
frontend/dist/bicep 2.56 kB
frontend/dist/Billing 1.34 kB
frontend/dist/BillingSection 21.6 kB
frontend/dist/birb 145 B
frontend/dist/bison 180 B
frontend/dist/bnf 144 B
frontend/dist/BoxPlot 5.86 kB
frontend/dist/brainfuck 150 B
frontend/dist/brightscript 153 B
frontend/dist/bro 144 B
frontend/dist/browserAll-0QZMN1W2 37.4 kB
frontend/dist/bsl 144 B
frontend/dist/ButtonPrimitives 1.41 kB
frontend/dist/c-like 5.27 kB
frontend/dist/c 142 B
frontend/dist/cal 1.12 kB
frontend/dist/CalendarHeatMap 5.67 kB
frontend/dist/cameligo 2.2 kB
frontend/dist/capnproto 974 B
frontend/dist/ceylon 1.24 kB
frontend/dist/cfscript 149 B
frontend/dist/chaiscript 219 B
frontend/dist/changeRequestsLogic 1.39 kB
frontend/dist/cil 144 B
frontend/dist/clean 671 B
frontend/dist/CLIAuthorize 12.2 kB
frontend/dist/clike 146 B
frontend/dist/CLILive 4.85 kB
frontend/dist/clojure 3.76 kB
frontend/dist/clojure-repl 326 B
frontend/dist/cmake 146 B
frontend/dist/cobol 146 B
frontend/dist/coffee 3.6 kB
frontend/dist/coffeescript 153 B
frontend/dist/Cohort 23.8 kB
frontend/dist/CohortCalculationHistory 7.09 kB
frontend/dist/Cohorts 10.3 kB
frontend/dist/concurnas 150 B
frontend/dist/ConfirmOrganization 5.35 kB
frontend/dist/conversations.js 64.5 kB
frontend/dist/coq 3.61 kB
frontend/dist/core 315 B
frontend/dist/cos 1.46 kB
frontend/dist/Coupons 1.58 kB
frontend/dist/cpp 5.31 kB
frontend/dist/Create 1.52 kB
frontend/dist/crisp-chat-integration.js 2.11 kB
frontend/dist/crmsh 1.53 kB
frontend/dist/crystal 182 B
frontend/dist/csharp 147 B
frontend/dist/cshtml 181 B
frontend/dist/csp 571 B
frontend/dist/css 4.51 kB
frontend/dist/css-extras 151 B
frontend/dist/cssMode 4.18 kB
frontend/dist/csv 144 B
frontend/dist/CustomCssScene 4.41 kB
frontend/dist/CustomerAnalyticsConfigurationScene 2.85 kB
frontend/dist/CustomerAnalyticsScene 29.5 kB
frontend/dist/customizations.full.js 18.7 kB
frontend/dist/CyclotronJobInputAssignee 2.19 kB
frontend/dist/CyclotronJobInputTicketTags 1.57 kB
frontend/dist/cypher 3.4 kB
frontend/dist/d 142 B
frontend/dist/dart 4.26 kB
frontend/dist/Dashboard 1.79 kB
frontend/dist/Dashboards 15.2 kB
frontend/dist/DataManagementScene 1.5 kB
frontend/dist/DataPipelinesNewScene 3.15 kB
frontend/dist/DataWarehouseScene 1.5 kB
frontend/dist/DataWarehouseSourceScene 1.48 kB
frontend/dist/dataweave 150 B
frontend/dist/dax 144 B
frontend/dist/Deactivated 1.99 kB
frontend/dist/dead-clicks-autocapture.js 13.1 kB
frontend/dist/DeadLetterQueue 6.26 kB
frontend/dist/DebugScene 19.4 kB
frontend/dist/decompressionWorker 2.85 kB
frontend/dist/decompressionWorker.js 2.85 kB
frontend/dist/DefinitionEdit 7.98 kB
frontend/dist/DefinitionView 23.1 kB
frontend/dist/delphi 2.1 kB
frontend/dist/DestinationsScene 3.54 kB
frontend/dist/dhall 146 B
frontend/dist/diff 145 B
frontend/dist/dist 575 B
frontend/dist/django 181 B
frontend/dist/dns 1.88 kB
frontend/dist/dns-zone-file 154 B
frontend/dist/docker 147 B
frontend/dist/dockerfile 1.88 kB
frontend/dist/dos 1.3 kB
frontend/dist/dot 144 B
frontend/dist/dsconfig 724 B
frontend/dist/dts 1.47 kB
frontend/dist/dust 585 B
frontend/dist/EarlyAccessFeature 1.57 kB
frontend/dist/EarlyAccessFeatures 3.7 kB
frontend/dist/ebnf 145 B
frontend/dist/ecl 5.35 kB
frontend/dist/editorconfig 153 B
frontend/dist/EditorScene 1.62 kB
frontend/dist/eiffel 147 B
frontend/dist/ejs 178 B
frontend/dist/elixir 10.3 kB
frontend/dist/elm 144 B
frontend/dist/EmailMFAVerify 3.84 kB
frontend/dist/EndpointScene 30.9 kB
frontend/dist/EndpointsScene 21.9 kB
frontend/dist/erb 344 B
frontend/dist/erlang 2.09 kB
frontend/dist/erlang-repl 1.01 kB
frontend/dist/ErrorTrackingConfigurationScene 3.05 kB
frontend/dist/ErrorTrackingIssueFingerprintsScene 6.18 kB
frontend/dist/ErrorTrackingIssueScene 86.8 kB
frontend/dist/ErrorTrackingScene 13.8 kB
frontend/dist/etlua 214 B
frontend/dist/EvaluationTemplates 1.46 kB
frontend/dist/EventsScene 3.3 kB
frontend/dist/excel-formula 154 B
frontend/dist/excel 4.45 kB
frontend/dist/exception-autocapture.js 11.9 kB
frontend/dist/Experiment 273 kB
frontend/dist/Experiments 18.6 kB
frontend/dist/ExportsScene 4.73 kB
frontend/dist/factor 147 B
frontend/dist/false 146 B
frontend/dist/FeatureFlag 98.7 kB
frontend/dist/FeatureFlags 1.42 kB
frontend/dist/firestore-security-rules 165 B
frontend/dist/fix 529 B
frontend/dist/FlappyHog 6.65 kB
frontend/dist/flix 756 B
frontend/dist/flow 145 B
frontend/dist/flow9 1.81 kB
frontend/dist/fortran 148 B
frontend/dist/freemarker2 16.7 kB
frontend/dist/fsharp 2.99 kB
frontend/dist/ftl 178 B
frontend/dist/gams 3.17 kB
frontend/dist/gap 144 B
frontend/dist/gauss 13.1 kB
frontend/dist/gcode 146 B
frontend/dist/gdscript 149 B
frontend/dist/gedcom 147 B
frontend/dist/gherkin 670 B
frontend/dist/git 144 B
frontend/dist/glsl 179 B
frontend/dist/gml 144 B
frontend/dist/gn 143 B
frontend/dist/go 143 B
frontend/dist/go-module 150 B
frontend/dist/golo 677 B
frontend/dist/gradle 1.68 kB
frontend/dist/graphql 2.27 kB
frontend/dist/groovy 1.73 kB
frontend/dist/Group 15.2 kB
frontend/dist/Groups 4.79 kB
frontend/dist/GroupsNew 8.21 kB
frontend/dist/haml 179 B
frontend/dist/handlebars 2.51 kB
frontend/dist/haskell 1.82 kB
frontend/dist/haxe 2.01 kB
frontend/dist/hcl 3.6 kB
frontend/dist/HealthScene 11.9 kB
frontend/dist/HeatmapNewScene 5.03 kB
frontend/dist/HeatmapRecordingScene 4.79 kB
frontend/dist/HeatmapScene 6.9 kB
frontend/dist/HeatmapsScene 4.74 kB
frontend/dist/hlsl 179 B
frontend/dist/HogFunctionScene 59.7 kB
frontend/dist/HogRepl 8.23 kB
frontend/dist/hoon 145 B
frontend/dist/hpkp 145 B
frontend/dist/hsp 3.51 kB
frontend/dist/hsts 145 B
frontend/dist/html 5.6 kB
frontend/dist/htmlbars 2.62 kB
frontend/dist/htmlMode 4.64 kB
frontend/dist/http 1.04 kB
frontend/dist/hy 3.08 kB
frontend/dist/ichigojam 150 B
frontend/dist/icon 145 B
frontend/dist/icu-message-format 159 B
frontend/dist/idris 180 B
frontend/dist/iecst 146 B
frontend/dist/ignore 147 B
frontend/dist/image-blob-reduce.esm 49.4 kB
frontend/dist/InboxScene 53.1 kB
frontend/dist/index 253 kB
frontend/dist/index.js 253 kB
frontend/dist/inform7 802 B
frontend/dist/ini 1.11 kB
frontend/dist/InsightOptions 5.62 kB
frontend/dist/InsightScene 27.5 kB
frontend/dist/IntegrationsRedirect 1.59 kB
frontend/dist/intercom-integration.js 2.16 kB
frontend/dist/InviteSignup 14.2 kB
frontend/dist/io 143 B
frontend/dist/irpf90 4.94 kB
frontend/dist/isbl 83.8 kB
frontend/dist/j 142 B
frontend/dist/java 2.69 kB
frontend/dist/javadoc 216 B
frontend/dist/javadoclike 152 B
frontend/dist/javascript 996 B
frontend/dist/javastacktrace 155 B
frontend/dist/jboss-cli 1.02 kB
frontend/dist/jexl 145 B
frontend/dist/jolie 146 B
frontend/dist/jq 143 B
frontend/dist/js-extras 150 B
frontend/dist/js-templates 153 B
frontend/dist/jsdoc 214 B
frontend/dist/json 714 B
frontend/dist/json5 180 B
frontend/dist/jsonMode 13.9 kB
frontend/dist/jsonp 180 B
frontend/dist/jsstacktrace 153 B
frontend/dist/jsx 144 B
frontend/dist/julia-repl 353 B
frontend/dist/julia 7.24 kB
frontend/dist/keepalived 151 B
frontend/dist/keyman 147 B
frontend/dist/kotlin 147 B
frontend/dist/kumir 146 B
frontend/dist/kusto 146 B
frontend/dist/lasso 3.07 kB
frontend/dist/latex 3.68 kB
frontend/dist/latte 214 B
frontend/dist/lazy 153 kB
frontend/dist/ldif 475 B
frontend/dist/leaf 564 B
frontend/dist/LegacyPluginScene 21.9 kB
frontend/dist/LemonDialog 1.33 kB
frontend/dist/less 7.7 kB
frontend/dist/lexon 2.45 kB
frontend/dist/lib 2.23 kB
frontend/dist/lilypond 183 B
frontend/dist/Link 1.32 kB
frontend/dist/LinkScene 25.7 kB
frontend/dist/LinksScene 5.06 kB
frontend/dist/liquid 181 B
frontend/dist/lisp 1.27 kB
frontend/dist/livecodeserver 8.34 kB
frontend/dist/LiveDebugger 19.9 kB
frontend/dist/LiveEventsTable 5.3 kB
frontend/dist/livescript 3.54 kB
frontend/dist/LLMAnalyticsClusterScene 16.6 kB
frontend/dist/LLMAnalyticsClustersScene 43.3 kB
frontend/dist/LLMAnalyticsDatasetScene 20.6 kB
frontend/dist/LLMAnalyticsDatasetsScene 4.14 kB
frontend/dist/LLMAnalyticsEvaluation 52.5 kB
frontend/dist/LLMAnalyticsEvaluationsScene 30.3 kB
frontend/dist/LLMAnalyticsPlaygroundScene 29.9 kB
frontend/dist/LLMAnalyticsScene 49.4 kB
frontend/dist/LLMAnalyticsSessionScene 14.1 kB
frontend/dist/LLMAnalyticsTraceScene 98.7 kB
frontend/dist/LLMAnalyticsUsers 1.38 kB
frontend/dist/LLMASessionFeedbackDisplay 5.7 kB
frontend/dist/LLMPromptScene 23.6 kB
frontend/dist/LLMPromptsScene 4.38 kB
frontend/dist/llvm 145 B
frontend/dist/log 144 B
frontend/dist/Login 9.23 kB
frontend/dist/Login2FA 5.07 kB
frontend/dist/logs.js 39 kB
frontend/dist/LogsScene 12.6 kB
frontend/dist/lolcode 148 B
frontend/dist/lsl 12 kB
frontend/dist/lua 2 kB
frontend/dist/m3 2.82 kB
frontend/dist/magma 146 B
frontend/dist/makefile 1.2 kB
frontend/dist/ManagedMigration 14.9 kB
frontend/dist/markdown 3.79 kB
frontend/dist/MarketingAnalyticsScene 24.3 kB
frontend/dist/markup-templating 158 B
frontend/dist/markup 147 B
frontend/dist/MaterializedColumns 11.1 kB
frontend/dist/mathematica 113 kB
frontend/dist/matlab 147 B
frontend/dist/Max 1.69 kB
frontend/dist/maxima 28.8 kB
frontend/dist/maxscript 150 B
frontend/dist/mdx 5.38 kB
frontend/dist/mel 16.7 kB
frontend/dist/mercury 2.19 kB
frontend/dist/mermaid 148 B
frontend/dist/MessageTemplate 17.1 kB
frontend/dist/mips 2.59 kB
frontend/dist/mipsasm 2.58 kB
frontend/dist/mizar 856 B
frontend/dist/ModelsScene 2.56 kB
frontend/dist/mojolicious 443 B
frontend/dist/MonacoDiffEditor 403 B
frontend/dist/mongodb 148 B
frontend/dist/monkey 1.46 kB
frontend/dist/moonscript 151 B
frontend/dist/MoveToPostHogCloud 5.31 kB
frontend/dist/msdax 4.92 kB
frontend/dist/mysql 11.3 kB
frontend/dist/n1ql 3.12 kB
frontend/dist/n4js 145 B
frontend/dist/nand2tetris-hdl 156 B
frontend/dist/naniscript 151 B
frontend/dist/nasm 145 B
frontend/dist/NavTabChat 5.17 kB
frontend/dist/neon 145 B
frontend/dist/nevod 146 B
frontend/dist/NewSourceWizard 1.57 kB
frontend/dist/NewTabScene 1.5 kB
frontend/dist/nginx 1.51 kB
frontend/dist/nim 144 B
frontend/dist/nix 770 B
frontend/dist/node-repl 369 B
frontend/dist/NotebookCanvasScene 3.79 kB
frontend/dist/NotebookPanel 5.83 kB
frontend/dist/NotebookScene 8.81 kB
frontend/dist/NotebooksScene 8.42 kB
frontend/dist/nsis 145 B
frontend/dist/OAuthAuthorize 10.5 kB
frontend/dist/objective-c 2.42 kB
frontend/dist/objectivec 2.67 kB
frontend/dist/ocaml 146 B
frontend/dist/Onboarding 642 kB
frontend/dist/OnboardingCouponRedemption 2.05 kB
frontend/dist/opencl 181 B
frontend/dist/openqasm 149 B
frontend/dist/openscad 1.43 kB
frontend/dist/oxygene 2.06 kB
frontend/dist/oz 143 B
frontend/dist/parigp 147 B
frontend/dist/parser 147 B
frontend/dist/parser3 689 B
frontend/dist/pascal 3 kB
frontend/dist/pascaligo 150 B
frontend/dist/passkeyLogic 1.33 kB
frontend/dist/PasswordReset 5.18 kB
frontend/dist/PasswordResetComplete 3.79 kB
frontend/dist/pcaxis 147 B
frontend/dist/peoplecode 151 B
frontend/dist/perl 8.26 kB
frontend/dist/PersonScene 16.6 kB
frontend/dist/PersonsScene 5.32 kB
frontend/dist/pf 1.41 kB
frontend/dist/pgsql 19 kB
frontend/dist/php 8.03 kB
frontend/dist/php-extras 219 B
frontend/dist/php-template 576 B
frontend/dist/phpdoc 249 B
frontend/dist/PipelineStatusScene 7.09 kB
frontend/dist/pla 1.69 kB
frontend/dist/plaintext 268 B
frontend/dist/plsql 180 B
frontend/dist/pony 1.11 kB
frontend/dist/posthog 253 kB
frontend/dist/postiats 7.86 kB
frontend/dist/powerquery 151 B
frontend/dist/powershell 3.28 kB
frontend/dist/PreflightCheck 6.41 kB
frontend/dist/processing 151 B
frontend/dist/product-tours.js 118 kB
frontend/dist/ProductTour 308 kB
frontend/dist/ProductTours 5.57 kB
frontend/dist/profile 632 B
frontend/dist/ProjectHomepage 19.5 kB
frontend/dist/prolog 147 B
frontend/dist/promql 147 B
frontend/dist/properties 859 B
frontend/dist/protobuf 824 B
frontend/dist/psl 144 B
frontend/dist/pug 144 B
frontend/dist/puppet 147 B
frontend/dist/pure 145 B
frontend/dist/purebasic 1.74 kB
frontend/dist/purescript 185 B
frontend/dist/python 4.79 kB
frontend/dist/python-repl 375 B
frontend/dist/q 1.28 kB
frontend/dist/qml 144 B
frontend/dist/qore 145 B
frontend/dist/qsharp 147 B
frontend/dist/r 3.24 kB
frontend/dist/racket 181 B
frontend/dist/razor 9.36 kB
frontend/dist/reason 147 B
frontend/dist/reasonml 3.41 kB
frontend/dist/recorder-v2.js 113 kB
frontend/dist/recorder.js 113 kB
frontend/dist/redis 3.56 kB
frontend/dist/redshift 11.8 kB
frontend/dist/refractor 17.8 kB
frontend/dist/regex 146 B
frontend/dist/RegionMap 135 kB
frontend/dist/rego 145 B
frontend/dist/renpy 146 B
frontend/dist/ResourceTransfer 10 kB
frontend/dist/rest 145 B
frontend/dist/restructuredtext 3.91 kB
frontend/dist/RevenueAnalyticsScene 26.5 kB
frontend/dist/rib 1.44 kB
frontend/dist/rip 144 B
frontend/dist/roboconf 149 B
frontend/dist/robotframework 155 B
frontend/dist/routeros 2.66 kB
frontend/dist/rsl 1.2 kB
frontend/dist/ruby 8.51 kB
frontend/dist/ruleslanguage 3.98 kB
frontend/dist/rust 4.17 kB
frontend/dist/sas 144 B
frontend/dist/sass 145 B
frontend/dist/SavedInsights 1.51 kB
frontend/dist/sb 1.83 kB
frontend/dist/scala 1.68 kB
frontend/dist/scheme 147 B
frontend/dist/scilab 1.33 kB
frontend/dist/scss 145 B
frontend/dist/SdkDoctorScene 5.12 kB
frontend/dist/SessionAttributionExplorerScene 7.46 kB
frontend/dist/SessionGroupSummariesTable 5.49 kB
frontend/dist/SessionGroupSummaryScene 17.9 kB
frontend/dist/SessionProfileScene 16.8 kB
frontend/dist/SessionRecordingDetail 2.59 kB
frontend/dist/SessionRecordingFilePlaybackScene 5.33 kB
frontend/dist/SessionRecordings 1.59 kB
frontend/dist/SessionRecordingsKiosk 9.72 kB
frontend/dist/SessionRecordingsPlaylistScene 4.93 kB
frontend/dist/SessionRecordingsSettingsScene 2.76 kB
frontend/dist/SessionsScene 4.73 kB
frontend/dist/SettingsScene 3.85 kB
frontend/dist/SharedMetric 16.5 kB
frontend/dist/SharedMetrics 1.36 kB
frontend/dist/shell-session 188 B
frontend/dist/shell 3.08 kB
frontend/dist/SignupContainer 23.7 kB
frontend/dist/Site 2.05 kB
frontend/dist/smali 1.23 kB
frontend/dist/smalltalk 150 B
frontend/dist/smarty 181 B
frontend/dist/sml 1.27 kB
frontend/dist/solidity 149 B
frontend/dist/solution-file 154 B
frontend/dist/sophia 2.77 kB
frontend/dist/SourcesScene 4.01 kB
frontend/dist/sourceWizardLogic 1.51 kB
frontend/dist/soy 178 B
frontend/dist/sparql 181 B
frontend/dist/splunk-spl 151 B
frontend/dist/sqf 32.3 kB
frontend/dist/sql_more 12.4 kB
frontend/dist/sql 6.73 kB
frontend/dist/SqlVariableEditScene 8.11 kB
frontend/dist/squirrel 149 B
frontend/dist/st 7.41 kB
frontend/dist/stan 145 B
frontend/dist/StartupProgram 22 kB
frontend/dist/stata 16.8 kB
frontend/dist/step21 753 B
frontend/dist/stylus 147 B
frontend/dist/subunit 642 B
frontend/dist/SupportSettingsScene 26.5 kB
frontend/dist/SupportTicketScene 22.9 kB
frontend/dist/SupportTicketsScene 9.14 kB
frontend/dist/Survey 1.6 kB
frontend/dist/SurveyFormBuilder 2.4 kB
frontend/dist/Surveys 19.1 kB
frontend/dist/surveys.js 90.2 kB
frontend/dist/SurveyWizard 57.1 kB
frontend/dist/swift 7.62 kB
frontend/dist/systemd 148 B
frontend/dist/SystemStatus 17.8 kB
frontend/dist/systemverilog 7.62 kB
frontend/dist/t4-cs 214 B
frontend/dist/t4-templating 154 B
frontend/dist/t4-vb 248 B
frontend/dist/taggerscript 535 B
frontend/dist/tap 533 B
frontend/dist/TaskDetailScene 21 kB
frontend/dist/TaskTracker 17.2 kB
frontend/dist/tcl 3.57 kB
frontend/dist/textile 148 B
frontend/dist/thrift 744 B
frontend/dist/toml 145 B
frontend/dist/ToolbarLaunch 3.38 kB
frontend/dist/tp 1.6 kB
frontend/dist/tracing-headers.js 1.93 kB
frontend/dist/TransformationsScene 2.78 kB
frontend/dist/tremor 147 B
frontend/dist/tsMode 24 kB
frontend/dist/tsx 212 B
frontend/dist/tt2 178 B
frontend/dist/turtle 147 B
frontend/dist/twig 1.3 kB
frontend/dist/TwoFactorReset 4.84 kB
frontend/dist/typescript 151 B
frontend/dist/typespec 2.83 kB
frontend/dist/typoscript 151 B
frontend/dist/unrealscript 153 B
frontend/dist/Unsubscribe 2.48 kB
frontend/dist/uorazor 148 B
frontend/dist/uri 144 B
frontend/dist/UserInterview 5.4 kB
frontend/dist/UserInterviews 2.88 kB
frontend/dist/v 142 B
frontend/dist/vala 145 B
frontend/dist/vb 5.8 kB
frontend/dist/vbnet 180 B
frontend/dist/vbscript 1.83 kB
frontend/dist/vbscript-html 308 B
frontend/dist/velocity 149 B
frontend/dist/VercelConnect 4.9 kB
frontend/dist/VercelLinkError 2.77 kB
frontend/dist/VerifyEmail 5.34 kB
frontend/dist/verilog 148 B
frontend/dist/vhdl 1.81 kB
frontend/dist/vim 144 B
frontend/dist/vimMode 211 kB
frontend/dist/visual-basic 153 B
frontend/dist/VisualReviewRunScene 19.3 kB
frontend/dist/VisualReviewRunsScene 6.86 kB
frontend/dist/VisualReviewSettingsScene 9.97 kB
frontend/dist/warpscript 151 B
frontend/dist/wasm 145 B
frontend/dist/web-idl 148 B
frontend/dist/web-vitals.js 6.6 kB
frontend/dist/WebAnalyticsScene 6.57 kB
frontend/dist/WebGLRenderer-DYjOwNoG 60.3 kB
frontend/dist/WebGPURenderer-B_wkl_Ja 36.3 kB
frontend/dist/WebScriptsScene 3.4 kB
frontend/dist/webworkerAll-puPV1rBA 330 B
frontend/dist/wgsl 7.35 kB
frontend/dist/wiki 145 B
frontend/dist/Wizard 5.32 kB
frontend/dist/wolfram 148 B
frontend/dist/WorkflowScene 102 kB
frontend/dist/WorkflowsScene 47.2 kB
frontend/dist/WorldMap 5.54 kB
frontend/dist/wren 145 B
frontend/dist/x86asm 19.2 kB
frontend/dist/xeora 146 B
frontend/dist/xl 1.77 kB
frontend/dist/xml 2.14 kB
frontend/dist/xml-doc 148 B
frontend/dist/xojo 145 B
frontend/dist/xquery 147 B
frontend/dist/yaml 4.61 kB
frontend/dist/yang 145 B
frontend/dist/zephir 1.71 kB
frontend/dist/zig 144 B

compressed-size-action

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

14 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 1520 to 1542

actions.setTemplateExpanded(false)
actions.applyUrlTemplate(templateId)

// Apply intent after template so intent presets are not overwritten
if (!values.urlIntentApplied) {
const rawIntent = router.values.searchParams.intent
const intent = VALID_INTENTS.includes(rawIntent) ? (rawIntent as FlagIntent) : undefined
if (intent) {
actions.setFlagIntent(intent)
actions.applyUrlIntent()
if (intent === 'local-eval') {
actions.setFeatureFlag({
...values.featureFlag,
evaluation_runtime: FeatureFlagEvaluationRuntime.SERVER,
ensure_experience_continuity: false,
})
}
}
}
},
copyFlagSuccess: ({ featureFlagCopy }) => {
if (featureFlagCopy?.success.length) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplicated intent-application block (OnceAndOnlyOnce)

The same "read intent from URL, validate, call setFlagIntent / applyUrlIntent / setFeatureFlag" logic appears twice: once inside the loadFeatureFlag success handler (when no template is in the URL) and again at the end of the applyTemplate handler (when a template is present). Both blocks are character-for-character identical apart from the surrounding guard condition.

Extract a shared helper action (or shared-listener) to keep this logic in one place:

// Suggested extracted listener (illustrative)
applyUrlIntentIfNeeded: () => {
    if (values.urlIntentApplied) {
        return
    }
    const rawIntent = router.values.searchParams.intent
    const intent = VALID_INTENTS.includes(rawIntent) ? (rawIntent as FlagIntent) : undefined
    if (intent) {
        actions.setFlagIntent(intent)
        actions.applyUrlIntent()
        if (intent === 'local-eval') {
            actions.setFeatureFlag({
                ...values.featureFlag,
                evaluation_runtime: FeatureFlagEvaluationRuntime.SERVER,
                ensure_experience_continuity: false,
            })
        }
    }
},

Then both call sites become a single actions.applyUrlIntentIfNeeded(), eliminating the duplication. The same pattern already exists for applyUrlTemplate / urlTemplateApplied.

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!

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 2, 2026

Additional Comments (2)

frontend/src/scenes/feature-flags/featureFlagIntentWarningLogic.test.ts
Test mock objects cast with as any instead of using complete types

The cohort objects passed to cohortCreated are cast with as any (here and again at line 588–604). Per the project convention, mock objects should include all required properties from the interface so that type errors surface at compile time rather than silently at runtime.

Consider importing CohortType and providing the missing required fields directly instead of relying on the cast — this also protects the test against future interface changes that would otherwise go undetected.

Context Used: Rule from dashboard - When creating mock objects for tests, include all required properties from the interface to avoid ca... (source)

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!


frontend/src/products.tsx
FlagIntent literal duplicated instead of imported

The union 'local-eval' | 'first-page-load' is the canonical FlagIntent type defined in featureFlagIntentWarningLogic.ts. It is inlined here (and identically in products/feature_flags/manifest.tsx) rather than imported, so adding a new intent requires updates in three places.

featureFlagTemplateConstants.ts already imports FlagIntent from the logic file, so the same import pattern is possible here:

import type { FlagIntent } from 'scenes/feature-flags/featureFlagIntentWarningLogic'

Then the param type becomes simply intent?: FlagIntent.

@tests-posthog
Copy link
Contributor

tests-posthog bot commented Mar 2, 2026

Visual regression: Storybook UI snapshots updated

Changes: 2 snapshots (2 modified, 0 added, 0 deleted)

What this means:

  • Snapshots have been automatically updated to match current rendering
  • Next CI run will switch to CHECK mode to verify stability
  • If snapshots change again, CHECK mode will fail (indicates flapping)

Next steps:

  • Review the changes to ensure they're intentional
  • Approve if changes match your expectations
  • If unexpected, investigate component rendering

Review snapshot changes →

Copy link
Contributor

@dmarticus dmarticus left a comment

Choose a reason for hiding this comment

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

Nice work — the intent system is well-designed for extensibility and the test coverage is solid. A few comments inline, nothing blocking.

}

// Apply intent from URL param (when no template, or template already applied)
if (!templateId && featureFlag && !values.urlIntentApplied) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This 13-line block is duplicated verbatim in the applyTemplate listener below. If a new intent is added (e.g. 'edge-eval'), both sites need updating.

Consider extracting a helper:

function maybeApplyUrlIntent(values, actions) {
    if (values.urlIntentApplied) return
    const rawIntent = router.values.searchParams.intent
    const intent = VALID_INTENTS.includes(rawIntent) ? (rawIntent as FlagIntent) : undefined
    if (intent) {
        actions.setFlagIntent(intent)
        actions.applyUrlIntent()
        if (intent === 'local-eval') {
            actions.setFeatureFlag({
                ...values.featureFlag,
                evaluation_runtime: FeatureFlagEvaluationRuntime.SERVER,
                ensure_experience_continuity: false,
            })
        }
    }
}


const intentsEnabled = !!featureFlags[FEATURE_FLAGS.FEATURE_FLAG_CREATION_INTENTS]
const [selectedTemplate, setSelectedTemplate] = useState<FeatureFlagTemplate | null>(null)

Copy link
Contributor

Choose a reason for hiding this comment

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

Convention note: PostHog prefers kea over useState for scene-level components. featureFlagTemplatesSceneLogic already has selectedTemplate / setSelectedTemplate state that could be reused here, or a small dedicated logic could own this.

import { INTENT_KEYS, INTENT_METADATA } from 'products/feature_flags/frontend/featureFlagTemplateConstants'

type FeatureFlagTemplate = 'simple' | 'targeted' | 'multivariate' | 'targeted-multivariate'

Copy link
Contributor

Choose a reason for hiding this comment

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

This type and the TEMPLATE_METADATA below duplicate TemplateKey and TEMPLATE_NAMES from products/feature_flags/frontend/featureFlagTemplateConstants.ts, with slightly different labels ("Simple flag" here vs "Percentage rollout" there). The divergent names could confuse users navigating between the dropdown and the templates page. Consider importing TemplateKey and consolidating the metadata.

}

if (slowPropertyKeys.length === 1) {
issues.add(
Copy link
Contributor

Choose a reason for hiding this comment

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

When Group 1 has one slow property email (producing 'The property "email" won't be...') and Group 2 has two slow properties (producing '2 properties won't be...'), both messages appear because they are different strings. Users see both but can't tell which properties Group 2 refers to.

Consider collecting all slow properties across all groups first, then producing a single unified message — or always naming the properties regardless of count.

if (property.type === PropertyFilterType.Cohort) {
const cohortId = property.value
const cohort = cohortsById[cohortId] ?? cohortsById[String(cohortId)]
if (cohort?.is_static) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: The double lookup suggests cohortId can be either a number or a string. A brief comment explaining this known quirk would save future readers from wondering if it's a bug.

// cohortId may be numeric or stringified depending on filter source
const cohort = cohortsById[cohortId] ?? cohortsById[String(cohortId)]

}

const INTENT_CONSEQUENCE: Record<FlagIntent, string> = {
'local-eval':
Copy link
Contributor

Choose a reason for hiding this comment

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

Intent metadata is now scattered across three files:

  • INTENT_METADATA (name, description, icon) in featureFlagTemplateConstants.ts
  • INTENT_CONSEQUENCE here
  • intentDocUrl in featureFlagIntentWarningLogic.ts

Co-locating all of these in featureFlagTemplateConstants.ts would make adding a new intent a single-file change.

className: 'new-feature-flag-overlay',
actionable: true,
closeOnClickInside: false,
overlay: <OverlayForNewFeatureFlagMenu />,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: A brief comment explaining WHY this is needed (the intent submenu adds a second step inside the overlay) would help future maintainers who might otherwise remove it.

@dmarticus
Copy link
Contributor

Overall this is well-structured and the intent system is nicely designed for extensibility. I'd suggest addressing the duplicated intent-application block in featureFlagLogic.ts before merging — identical 13-line blocks in two listeners is a real maintenance risk when adding new intents. Everything else in the inline comments are nits/suggestions that can be follow-ups.

@ordehi ordehi force-pushed the feat/feature-flag-creation-intents branch from 7bf481f to 1449849 Compare March 5, 2026 13:11
ordehi and others added 18 commits March 5, 2026 13:02
…warnings

Add evaluation intent declaration to V2 flag creation flow. Users pick
an intent (local evaluation or first page load) and the form surfaces
contextual warnings scoped to that intent. Also adds unreachable
conditions detection as an always-on warning.

- New featureFlagIntentWarningLogic with structured per-group warnings
- Intent cards on the template scene (gated behind FEATURE_FLAG_CREATION_INTENTS)
- flagIntent action/reducer in featureFlagLogic with ?intent URL param
- Warning banners in FeatureFlagReleaseConditionsCollapsible
…aming

Integrate intent selection as a second step after template selection
instead of a separate section. Rename "First page load" to "Prevent
flicker" and rewrite warning copy to describe symptoms users experience.
For prevent-flicker intent, cohort and non-instant property warnings
now produce a single flicker_risk warning listing all issues instead
of separate banners per property. Local-eval warnings are also
deduplicated to one per category.
…ry property

Count slow properties and cohorts instead of enumerating each one, so
the warning stays compact regardless of how many filters are added.
…only ones

Property-only dynamic cohorts work fine for local evaluation. Now
checks cohort criteria types and only warns when behavioral filters
(events, sequences) are present. Static cohorts still always warn.
When intents are enabled, clicking a template in the dropdown drills
into an intent submenu (local eval, prevent flicker, skip). Uses
closeOnClickInside: false so the popover stays open during drill-down.
Remote config and experiment items still navigate directly. Also adds
intent param to featureFlagNew URL helper and regenerates products.tsx.
…ocal eval

Add flag-level warnings via a new flagWarnings selector. When the
local-eval intent is set and ensure_experience_continuity is enabled,
show a warning that this setting requires server-side state.
Replace per-warning banners with a single collapsible "N issues detected"
banner. Expanding shows a consequence message explaining the impact
(e.g., forced server request for local eval) followed by a bullet list
of detected issues and a single shared doc link.

- Simplify data model: logic produces flat string[] instead of
  ConditionWarning objects for intent issues
- Remove regex check (already handled by existing global warning)
- Improve "is not set" message to explain why it breaks local eval
- Unreachable conditions remain as inline banners per group
- Fix race condition: apply intent after template in applyTemplate listener
- Add idempotency guard (urlIntentApplied) to prevent re-application on reload
- Validate intent URL param against VALID_INTENTS before use
- Simplify unreachableGroups from O(n²) to O(n) with foundBroad flag
- Remove redundant feature flag gate from intentDocUrl selector
- Replace inline style with Tailwind min-h-[80vh]
- Consolidate intent metadata into single source of truth in
  featureFlagTemplateConstants.ts (removes duplicates from
  FeatureFlagTemplatesScene.tsx and NewFeatureFlagMenu.tsx)
- Add test coverage for static cohort, behavioral cohort, and
  singular/plural flicker property messages
…elper

maybeApplyUrlIntent() replaces two identical 13-line blocks in
loadFeatureFlagSuccess and applyTemplate listeners. Adding a new
intent now only requires updating VALID_INTENTS and the helper.
Move consequence strings and doc URLs into INTENT_METADATA in
featureFlagTemplateConstants.ts. Remove intentDocUrl selector and
INTENT_CONSEQUENCE constant. Adding a new intent is now a single-file
change.
…gMenu

Import TemplateKey from featureFlagTemplateConstants instead of
duplicating the type locally. Keep dropdown-specific display metadata
(shorter labels for compact menu).
Extract IntentWarningsBanner and UnreachableConditionBanner into
separate components that only mount featureFlagIntentWarningLogic
when flagId is provided. Fixes unintended side effects when
DefaultReleaseConditions renders FeatureFlagReleaseConditionsCollapsible
without a flag context.

Also fixes merge conflict artifact in FeatureFlagOverviewV2 (broken
render prop pattern), sentence casing in template names, and adds
explicit ComponentType import.
Add tests for unreachable groups + intent issues firing simultaneously,
invalid intent string producing no side effects, and a full test suite
for featureFlagTemplatesSceneLogic (selectedTemplate reducer,
intentsEnabled selector, afterMount redirect behavior).
Move IntentIssuesSummary expand/collapse state into
featureFlagIntentWarningLogic as issuesExpanded reducer and
toggleIssuesExpanded action, replacing local useState. Also fixes
setFeatureFlag parameter type in maybeApplyUrlIntent helper.
Move handleTemplateSelect business logic (analytics capture, conditional
navigation vs template selection) from FeatureFlagTemplatesScene
component body into featureFlagTemplatesSceneLogic as a selectTemplate
listener. Also co-locates navigateToNewFlag helper in the logic file.
The templates scene test added in a9ef2b4 imports kea-test-utils
but the product package.json was missing the dependency, breaking
the TypeScript check in CI.
@ordehi ordehi force-pushed the feat/feature-flag-creation-intents branch from 0804804 to d30c024 Compare March 5, 2026 17:04
@ordehi ordehi requested a review from dmarticus March 5, 2026 21:39
@github-project-automation github-project-automation bot moved this from In Review to Approved in Feature Flags Mar 9, 2026
@ordehi ordehi merged commit d8566ca into master Mar 9, 2026
155 checks passed
@github-project-automation github-project-automation bot moved this from Approved to Done in Feature Flags Mar 9, 2026
@ordehi ordehi deleted the feat/feature-flag-creation-intents branch March 9, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants