Skip to content

Target feature flags by build channel - #7859

Closed
lawrencecchen wants to merge 3 commits into
mainfrom
feat-nightly-flag-isolation
Closed

lawrencecchen wants to merge 3 commits into
mainfrom
feat-nightly-flag-isolation

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Adds cmux_build_channel to PostHog feature-flag evaluation as stable, nightly, or dev.

PostHog rules can now target Nightly independently from the production app. The SDK reloads flags only after the channel property is installed.

Test: ./scripts/lint-feature-flags.py.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Changes launch-time feature-flag fetch ordering and evaluation context; a mistake could cache wrong flag values per channel at startup, affecting gated release UI.

Overview
PostHog feature-flag evaluation now receives cmux_build_channel (stable, nightly, or dev) via setPersonPropertiesForFlags, so dashboard rules can roll flags out to one desktop release channel without affecting the others.

Startup ordering is tightened: SDK preloadFeatureFlags is off, CmuxFeatureFlags.start() no longer calls reloadFeatureFlags (that could run before the channel property was set), and PostHogAnalytics sets the channel property then performs a single reloadFeatureFlags after analytics setup. A small featureFlagEvaluationProperties(buildFlavor:) helper documents the PostHog person property; tests assert the three channel values.

Reviewed by Cursor Bugbot for commit 87080c7. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Enables build-channel targeting for PostHog feature flags so rules can target stable, nightly, or dev without affecting other channels. Defers flag loading until the build-channel context is set to avoid wrong early evaluations.

  • New Features

    • Sends cmux_build_channel for flag evaluation via featureFlagEvaluationProperties(buildFlavor:).
    • Disables SDK flag preloading (config.preloadFeatureFlags = false) and reloads flags only after setting the channel; CmuxFeatureFlags.start() no longer triggers an early PostHogSDK.reloadFeatureFlags().
  • Migration

    • In PostHog, target the cmux_build_channel person property with nightly, stable, or dev to scope rollouts by desktop release channel.

Written for commit 87080c7. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved feature-flag initialization during app launch to avoid race conditions and ensure flags are evaluated with the correct build channel.
    • Updated analytics startup to apply build-specific evaluation settings before triggering a feature-flag refresh.
  • Tests
    • Added unit test coverage confirming feature-flag evaluation properties are set correctly for stable, nightly, and development build flavors.

@vercel

vercel Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 11, 2026 6:59am
cmux-staging Building Building Preview, Comment Jul 11, 2026 6:59am

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

PostHog analytics now sets an explicit build-channel property before reloading feature flags. CmuxFeatureFlags applies flags from SDK payload callbacks instead of triggering an additional launch-time reload, with tests covering stable, nightly, and dev channels.

Changes

Feature flag initialization

Layer / File(s) Summary
Build-channel evaluation setup
Sources/PostHogAnalytics.swift, cmuxTests/PostHogAnalyticsPropertiesTests.swift
Analytics disables preloading, supplies cmux_build_channel before reloading flags, and tests stable, nightly, and dev values.
Payload-driven flag application
Sources/FeatureFlags.swift
Launch-time flag reloading is removed so loaded flags are applied through the SDK’s feature-flag payload callback.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PostHogAnalytics
  participant PostHogSDK
  participant CmuxFeatureFlags
  PostHogAnalytics->>PostHogSDK: Set cmux_build_channel
  PostHogAnalytics->>PostHogSDK: Reload feature flags
  PostHogSDK-->>CmuxFeatureFlags: Deliver feature-flag payload
  CmuxFeatureFlags->>CmuxFeatureFlags: Apply loaded flags
Loading

Possibly related PRs

  • manaflow-ai/cmux#7439: Reworks feature-flag loading and effective-value handling used by this initialization flow.
  • manaflow-ai/cmux#7592: Modifies PostHog feature-flag plumbing supporting release-gated Cloud VM UI.
  • manaflow-ai/cmux#7705: Adds feature-flag definitions and UI gating that rely on this PostHog loading flow.
🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (24 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.
Cmux Swift Actor Isolation ✅ Passed No new Swift 6 actor-isolation regression: the new helper is nonisolated, BuildFlavor is nonisolated Sendable, and the flag store still observes on MainActor.
Cmux Swift Blocking Runtime ✅ Passed PASS: The diff only adds config.preloadFeatureFlags = false; no new semaphores, waits, sleeps, syncs, polling, or locks were introduced.
Cmux Browser Automation Off-Main ✅ Passed Only Sources/PostHogAnalytics.swift changed; no browser.* socket commands or control-socket policy were touched, so the rule isn’t implicated.
Cmux Expensive Synchronous Load ✅ Passed The PR only disables PostHog feature-flag preloading on the analytics setup path; it adds no agent-history loads, JSON parsing, or main-actor/interactive sync work.
Cmux Cache Substitution Correctness ✅ Passed The change only adjusts transient PostHog flag evaluation: flags stay event-driven via didReceiveFeatureFlags, with build-channel props installed before reload; no persistence/history/snapshot cach...
Cmux No Hacky Sleeps ✅ Passed The diff only adds a Swift PostHog config flag; no non-Swift sleeps, timers, polling, or fixed waits are introduced, and the rule excludes Swift.
Cmux Algorithmic Complexity ✅ Passed Only new collection work is a 1-entry dictionary literal and fixed-size flag/test data; no nested scans or scalable rescans were introduced.
Cmux Swift Concurrency ✅ Passed The only actual diff adds config.preloadFeatureFlags = false; it does not introduce or expand DispatchQueue, Combine, completion-handler, or fire-and-forget Task patterns.
Cmux Swift @Concurrent ✅ Passed No changed async or actor-isolated code violates the rule: the new helper is synchronous, and UI-bound work already hops to MainActor/workQueue explicitly.
Cmux Swift File And Package Boundaries ✅ Passed Touched Swift files are under 400 lines, and the change stays in app-level PostHog/feature-flag glue rather than mixing unrelated responsibilities or needing a package split.
Cmux Swiftpm Lockfiles ✅ Passed Diff only touches Swift sources and tests; no Package.swift, Package.resolved, .gitignore, workflow, or Xcode project files changed.
Cmux Swift Logging ✅ Passed The diff adds no print/debugPrint/dump/NSLog, no ad hoc logging, and no new Logger constants; changed Swift code only adjusts PostHog flag setup.
Cmux User-Facing Error Privacy ✅ Passed The diff only changes PostHog analytics/feature-flag plumbing and tests; it adds no user-facing error, alert, or recovery copy.
Cmux Full Internationalization ✅ Passed Diff only adds config-token wiring, comments, and a test; no new user-facing Swift/web copy or locale-catalog changes were introduced.
Cmux Swiftui State Layout ✅ Passed PASS: The commit only changes PostHogAnalytics.swift; no SwiftUI view/state/layout code or new ObservableObject/@Published/GeometryReader patterns were introduced.
Cmux Architecture Rethink ✅ Passed The patch only sequences PostHog setup/reload before the existing feature-flag observer and names the invariant; it adds no sleeps, locks, or duplicate owners.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only changes PostHog analytics/feature-flag plumbing; no NSWindow/WindowGroup/cmuxAuxiliaryWindowIdentifiers changes, so the auxiliary-window shortcut rule isn’t triggered.
Cmux Source Artifacts ✅ Passed Only changed path is Sources/PostHogAnalytics.swift, a handwritten Swift source file; no logs, caches, temp dirs, or other artifact paths appear.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The PR only changes PostHog flag setup; the production diff adds no new debug/test-only member or guard in Sources/PostHogAnalytics.swift.
Cmux No Ambient Global State ✅ Passed The diff adds only a static helper on an existing owned type; no new file-scope funcs, mutable globals, or singleton state were introduced.
Title check ✅ Passed The title clearly summarizes the main change: targeting feature flags by build channel.
Description check ✅ Passed It covers summary and testing, but omits the template's demo video, review trigger, and checklist sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-nightly-flag-isolation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR lets PostHog feature flags target cmux builds by release channel. The main changes are:

  • Adds cmux_build_channel for feature-flag evaluation.
  • Reloads feature flags after the build-channel property is installed.
  • Removes the earlier startup reload from feature flag initialization.
  • Adds tests for stable, nightly, and dev channel values.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
Sources/FeatureFlags.swift Stops feature flag startup from triggering an early PostHog reload.
Sources/PostHogAnalytics.swift Installs the build-channel evaluation property before the first manual feature flag reload.
cmuxTests/PostHogAnalyticsPropertiesTests.swift Checks the typed channel property values for each supported build flavor.

Reviews (3): Last reviewed commit: "Defer feature flag preload until channel..." | Re-trigger Greptile

Comment on lines +164 to +166
#expect(PostHogAnalytics.featureFlagEvaluationProperties(buildFlavor: .stable) == [
"cmux_build_channel": "stable",
])

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.

P1 Any Dictionary Equality Fails

When this test compares featureFlagEvaluationProperties(...) to a dictionary literal, the left side is [String: Any]. Swift only provides dictionary equality when the value type is Equatable, so this can fail the test target at compile time instead of checking the channel value.

Suggested change
#expect(PostHogAnalytics.featureFlagEvaluationProperties(buildFlavor: .stable) == [
"cmux_build_channel": "stable",
])
let stableProperties = PostHogAnalytics.featureFlagEvaluationProperties(buildFlavor: .stable)
#expect(stableProperties["cmux_build_channel"] as? String == "stable")

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!

// CmuxFeatureFlags installs its observer during app launch, before
// analytics initialization reaches this point. Reload only after the
// build-channel evaluation context is ready.
PostHogSDK.shared.reloadFeatureFlags()

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.

P1 First Reload Can Race

App launch starts analytics asynchronously, then starts CmuxFeatureFlags, whose startup path also calls reloadFeatureFlags(). That earlier reload can run before this work-queue block installs cmux_build_channel, so the first flag notification can apply Stable/default targeting to a Nightly app until this later reload corrects it.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afc602544f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// CmuxFeatureFlags installs its observer during app launch, before
// analytics initialization reaches this point. Reload only after the
// build-channel evaluation context is ready.
PostHogSDK.shared.reloadFeatureFlags()

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 Badge Avoid racing the channel-scoped flag reload

When CmuxFeatureFlags.shared.start() runs immediately after PostHogAnalytics.shared.startIfNeeded() in AppDelegate, analytics setup is still asynchronous on the utility queue, and CmuxFeatureFlags.start() still calls PostHogSDK.shared.reloadFeatureFlags() before this new cmux_build_channel property is installed. If that reload reaches the SDK after setup has created remoteConfig but before setPersonPropertiesForFlags runs, the manual reload added here can be ignored while the earlier request is loading, leaving channel-targeted PostHog rules evaluated without the channel for that launch. Defer/remove the earlier reload or start feature flags only after this property has been installed.

Useful? React with 👍 / 👎.

@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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 87080c7. Configure here.

PostHogSDK.shared.reloadFeatureFlags()
// PostHogAnalytics reloads after installing its build-channel
// evaluation context. Starting a request here would race that setup
// during launch and could cache a result for the wrong channel.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Observer misses initial flag reload

Medium Severity

Removing the startup reloadFeatureFlags() from CmuxFeatureFlags.start() leaves the only reload on PostHog’s async work queue, while AppDelegate still enqueues analytics before registering the didReceiveFeatureFlags observer. A fast reload (e.g. cached flags) can finish first, so applyLoadedFlags() never runs and CmuxFeatureFlags can keep defaults for the whole session.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 87080c7. Configure here.

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 87080c70 Deployed Jul 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants