feat(cmux-next): Labs flags, one registry for opt-in looks and behaviors (hq#1980, no bead)

Closed
TE
22d ago
+2359-99

Summary

Labs (hq#1980): new looks and behaviors ship behind a flag that a person turns on, not in the defaults. This builds on the existing labs.previewFeatures and keeps it as the only mechanism. Its users (#20429's Now Playing card) keep reading snapshot.previewFeatures.

  • Registry: each row of schemas/settings/source/labs.json is a LabsFlag with owner, created, expires (about 14 days), cohort (off / nightly-next / default) and a one-line intent (its help). gen-settings-sources.py checks the fields and generates the Swift LabsFlag enum and webviews/src/experiments/labs.generated.ts. It prints warning: labs.<name> ... expired for any flag past its expiry, including under --check; the warning never fails the run.
  • Defaults: every flag defaults off. nightly-next flags are on in nightly and dev builds only. Promoting a flag to default is a one-line PR, and that PR is the design call.
  • Where flags show up: Settings > Experimental > Labs (a toggle with the intent line), the palette's settings scope, Help > Feature Flags (now bound, opens that list), cmux.json, MDM, and the agent policy.
  • New Tab page, team builds only: one quiet line, "Labs" (or "Labs (N on)"), on the page's column edges and type scale, collapsed by default and remembered per viewer. Open, it lists each flag as a 28px row: name, one-line intent (truncated before the badge), a "New" or "Expires soon" hint, and a switch; recently toggled first, else newest. Right-click a row for Turn On/Off and All Labs…. "All Labs…" opens Settings > Labs. A toggle is newTab.setLab, saved through the schema. Release and RC builds show nothing. Variant A of three, picked by cg-design-judge (https://github.com/manaflow-ai/cmux/pull/20452#issuecomment-6180365005).
  • Palette: "Labs…" (Help > Feature Flags is now Help > Labs…; "feature flags" stays a search keyword) opens Settings > Labs.
  • One helper per side:
    • Swift: snapshot.lab(.name).
    • Web: useLabsFlag("name") / labsFlag("name"), which read data-labs on <html>. Every PageWebView page gets that attribute, kept current. labsTeamBuild() reads data-labs-team.
  • Docs: skills/cmux-architecture/references/labs-flags.md explains how to put a change behind Labs.

Testing

  • Swift

    • LabsFlagTests: the cohort and file decide a flag, a bad value is off with a diagnostic, and every flag is a toggle with its fields.
    • NewTabLabsTests: a page toggle is saved in cmux.json, and a name that is not a flag writes nothing; All Labs… runs help.featureFlags.
    • Red: the fleet run-swift-testing-suites.sh at the red commits fails (no member 'lab'; the New Tab red is a compile red, since its API did not exist yet).
    • Green: fleet run-swift-testing-suites.sh on the merged head 18a0888e: 288 suites passed, 0 failed (the step exits 1 only because AcpmuxBundleReplacedTests skips without a published same-tree cmux-tui for that SHA).
    • Settings export on the merged head: no diff (exportIsFresh passes).
  • Web: bun test test/new-tab-labs.test.tsx was red at e59433fd (no section) and is green now. src/experiments/labs.test.ts passes.

  • Checks:

    • gen-settings-sources.py --check, gen-strings.mjs --check agentPane, ui-rules, and scoped vp check pass.
    • In bun run check, every step before the final whole-repo vp check passes. That last lint was killed on big-red (143); CI react-apps-check covers it.
    • crash ratchet, concurrency, system-blue and the scoped god-file check pass. App size is at the cap (113801).
  • Review (subagent): the agent pane's own web view (agent.pageHost off, the default) never got data-labs; fixed with AgentPaneLabs. The generator now rejects a key that is not labs.<name>. Known limit: a page's first moments can carry the flag list from when its web view was made; useLabsFlag follows the live value.

  • Localization: the two new New Tab strings are in all 21 locales. Flag intents show in English on the New Tab page (team builds only); Settings shows them localized.

  • Redesign (variant A): test/new-tab-labs.test.tsx was red first (3a5f675, 3a48dc6) and is green: collapsed by default as "Labs" / "Labs (N on)", rows with intent and switch, New / Expires soon hints, recently toggled first with a storage fallback, remembered open state, row menu. agent-pane-no-capsules, theme-colors, subtle-elevation and ui-rules pass locally. Release compile BUILD SUCCEEDED on d69557f.

Changelog

Added: Labs: opt-in flags for new looks and behaviors, listed in Settings > Experimental > Labs, the palette ("Labs…") and, in nightly and dev builds, a quick-toggle line on the New Tab page

Proof

Real cmux NEXT DEV builds from the fleet (--builder mac), on capture slots; host chip masked. Base: the New Tab page and palette before this PR. Head: this branch. The switch knob in these stills is white; since then it takes the page's theme color (theme-colors/subtle-elevation rules), otherwise unchanged.

LightDark
Before: New Tab
After: Labs collapsed
After: Labs open
After: row right-click menu
Before: palette "Labs"
After: palette "Labs…"

Open, close, reopen, then turn the flag on and off (head, light):

labs toggle

Full quality:

🤖 Generated with Claude Code