Skip to content

feat(feature-flags): migrate 3 env-flags to AppConfig-backed runtime flags#5086

Merged
TheodoreSpeaks merged 3 commits into
stagingfrom
feat/migrate-ff
Jun 16, 2026
Merged

feat(feature-flags): migrate 3 env-flags to AppConfig-backed runtime flags#5086
TheodoreSpeaks merged 3 commits into
stagingfrom
feat/migrate-ff

Conversation

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator

Summary

  • Migrates isTablesFractionalOrderingEnabled, isMothershipBetaFeaturesEnabled, and isWorkflowColumnsEnabledClient from startup-time env gates in env-flags.ts to AppConfig-backed runtime flags in feature-flags.ts
  • Fixes feature-flags.ts to match the existing AppConfig JSON schema: flag names are top-level keys (no flags wrapper), and admin gating is adminEnabled not admins
  • workflow-columns flag moves from a NEXT_PUBLIC_* build-time bake to server-side resolution in the table page, threaded as a prop to NewColumnDropdown
  • mothership-beta flag now resolves per-user in WorkspaceVfs.materialize where userId is in scope
  • tables-fractional-ordering flag replaced inline at each async call site in rows/service.ts and rows/ordering.ts; buildRowOrderBySql stays synchronous via a fractionalOrderingEnabled: boolean parameter
  • Test mocks updated from the removed env-flag to isFeatureEnabled returning false

AppConfig document format (freeform profile, no flags wrapper):

{
  "tables-fractional-ordering": { "enabled": true, "adminEnabled": false, "orgIds": [], "userIds": [] },
  "mothership-beta":             { "enabled": true, "adminEnabled": false, "orgIds": [], "userIds": [] },
  "workflow-columns":            { "enabled": true, "adminEnabled": false, "orgIds": [], "userIds": [] }
}

Type of Change

  • New feature

Testing

Tested manually. Lint and check:api-validation:strict pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 16, 2026 2:58am

Request Review

@cursor

cursor Bot commented Jun 16, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Runtime async flag checks affect table ordering semantics and copilot VFS/doc compile paths; per-user targeting only applies where userId is passed (not on API serve/alias resolution), which can surprise rollout if AppConfig uses org/user allowlists for mothership-beta.

Overview
Centralizes two capabilities in feature-flags.ts with env fallbacks (TABLES_FRACTIONAL_ORDERING, MOTHERSHIP_BETA_FEATURES): tables-fractional-ordering and mothership-beta. AppConfig payloads are now top-level flag keys (no flags wrapper), and admin gating uses adminEnabled instead of admins.

Table row ordering no longer reads isTablesFractionalOrderingEnabled at startup. Inserts, deletes, batch import, and queryRows / findRowMatches call await isFeatureEnabled('tables-fractional-ordering') so order_key vs legacy position behavior is resolved at runtime.

Mothership beta replaces isMothershipBetaFeaturesEnabled across copilot VFS, workflow plan aliases, sandbox file mounts, and doc tooling. WorkspaceVFS.materialize resolves the flag once per materialize with { userId } and stores _betaEnabled; other paths (e.g. getE2BDocFormat, resolveWorkflowAliasForWorkspace) evaluate without user context, so xlsx and alias resolution follow global/fallback rules only.

getE2BDocFormat is async and gates .xlsx on mothership-beta; all serve, compiled-check, compile, and VFS read paths were updated to await it.

UI: NewColumnDropdown drops NEXT_PUBLIC_WORKFLOW_COLUMNS_ENABLED / client env gating and always shows Enrichments plus full COLUMN_TYPE_OPTIONS (including Workflow).

Tests mock isFeatureEnabled instead of removed env flags; feature-flags.test.ts matches the new config shape.

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

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

Comment thread apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx Outdated
Comment thread apps/sim/lib/copilot/vfs/workflow-alias-resolver.ts
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Migrates three startup-time env flags (isTablesFractionalOrderingEnabled, isMothershipBetaFeaturesEnabled, isWorkflowColumnsEnabledClient) to AppConfig-backed runtime flags in feature-flags.ts, and fixes the schema mismatch (removes flags wrapper, renames adminsadminEnabled). The migration also makes getE2BDocFormat async to gate xlsx support on the runtime flag, threads workflowColumnsEnabled as a server-resolved prop to the table page, and caches the mothership-beta decision in WorkspaceVfs._betaEnabled (resolved once per materialize with correct userId).

  • tables-fractional-ordering: Flag check moved inline at each async ordering call site in ordering.ts and service.ts; buildRowOrderBySql stays synchronous via a fractionalOrderingEnabled boolean parameter.
  • mothership-beta: Correctly threaded with userId in WorkspaceVfs.materialize, but called without any user context in getE2BDocFormat, resolveInputFiles, and resolveWorkflowAliasForWorkspace — per-user AppConfig targeting won't apply to those paths.
  • workflow-columns: Resolved server-side in the table page and passed as a prop, but Table's embedded mode defaults to false, so the mothership chat panel embed will always hide workflow columns regardless of the flag value.

Confidence Score: 4/5

Safe to merge for deployments using global enable/disable flags; per-user targeting of mothership-beta will be silently incomplete in three call paths.

The core migration is well-structured: AppConfig schema fixes are correct, fallback env vars are preserved, and the fractional-ordering flag is consistently resolved at each async call site. The WorkspaceVfs pattern (resolve once with userId, cache on the instance) is the right model. The two gaps worth watching are: getE2BDocFormat, resolveInputFiles, and resolveWorkflowAliasForWorkspace strip user identity from the mothership-beta check, so per-user targeting via AppConfig userIds would silently not apply to those paths; and the embedded Table always receives workflowColumnsEnabled: false since no embedding server component threads the flag value through.

doc-compile.ts, function-execute.ts, and workflow-alias-resolver.ts for the missing userId context; table.tsx for embedded-mode flag propagation.

Important Files Changed

Filename Overview
apps/sim/lib/core/config/feature-flags.ts Schema corrected (removes flags wrapper, renames adminsadminEnabled), three new flags registered with env fallbacks. Logic looks correct; AppConfig fetch is cached (~30s TTL) so multiple isFeatureEnabled calls per request are cheap.
apps/sim/lib/copilot/tools/server/files/doc-compile.ts getE2BDocFormat made async to gate xlsx on the runtime flag; all call sites updated. However isFeatureEnabled('mothership-beta') is called without a userId, making per-user AppConfig targeting ineffective for xlsx compilation paths.
apps/sim/lib/copilot/vfs/workspace-vfs.ts Beta flag resolved once in materialize() with correct userId threading, stored as _betaEnabled; all internal usages reference the cached field — clean pattern.
apps/sim/lib/copilot/tools/handlers/function-execute.ts isFeatureEnabled('mothership-beta') called without userId in resolveInputFiles; per-user targeting won't apply to the file/folder listing path.
apps/sim/lib/copilot/vfs/workflow-alias-resolver.ts isFeatureEnabled('mothership-beta') called without userId; workflow alias resolution won't honor per-user AppConfig targeting.
apps/sim/lib/table/rows/ordering.ts Flag check migrated to isFeatureEnabled at each async call site; multiple calls per operation are cheap given AppConfig caching. Logic preserved correctly.
apps/sim/lib/table/rows/service.ts buildRowOrderBySql cleanly converted to accept fractionalOrderingEnabled: boolean; flag resolved before each query. Promise.all used in queryRows to parallelize the pending-delete mask fetch with the flag lookup — good pattern.
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx workflowColumnsEnabled prop added and threaded through; but it defaults to false, meaning embedded usages (e.g. mothership chat panel) silently lose workflow column visibility.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser
    participant TablePage as TablePage (RSC)
    participant FF as feature-flags.ts
    participant AC as AppConfig (~30s TTL)
    participant Table as Table (Client)
    participant VFS as WorkspaceVfs
    participant DC as getE2BDocFormat

    Browser->>TablePage: GET /tables/[id]
    TablePage->>FF: "isFeatureEnabled('workflow-columns', {userId})"
    FF->>AC: fetchAppConfigProfile (cached)
    AC-->>FF: FeatureFlagsConfig
    FF-->>TablePage: true/false
    TablePage->>Table: workflowColumnsEnabled prop
    Table-->>Browser: renders (workflow cols visible or hidden)

    Note over VFS: materialize(workspaceId, userId)
    VFS->>FF: "isFeatureEnabled('mothership-beta', {userId})"
    FF->>AC: fetchAppConfigProfile (cached)
    AC-->>FF: FeatureFlagsConfig
    FF-->>VFS: betaEnabled (per-user check works)
    VFS->>VFS: _betaEnabled stored for request

    Note over DC: getE2BDocFormat(fileName)
    DC->>FF: isFeatureEnabled('mothership-beta') no userId
    FF->>AC: fetchAppConfigProfile (cached)
    AC-->>FF: FeatureFlagsConfig
    FF-->>DC: false if userIds-only targeting
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Browser
    participant TablePage as TablePage (RSC)
    participant FF as feature-flags.ts
    participant AC as AppConfig (~30s TTL)
    participant Table as Table (Client)
    participant VFS as WorkspaceVfs
    participant DC as getE2BDocFormat

    Browser->>TablePage: GET /tables/[id]
    TablePage->>FF: "isFeatureEnabled('workflow-columns', {userId})"
    FF->>AC: fetchAppConfigProfile (cached)
    AC-->>FF: FeatureFlagsConfig
    FF-->>TablePage: true/false
    TablePage->>Table: workflowColumnsEnabled prop
    Table-->>Browser: renders (workflow cols visible or hidden)

    Note over VFS: materialize(workspaceId, userId)
    VFS->>FF: "isFeatureEnabled('mothership-beta', {userId})"
    FF->>AC: fetchAppConfigProfile (cached)
    AC-->>FF: FeatureFlagsConfig
    FF-->>VFS: betaEnabled (per-user check works)
    VFS->>VFS: _betaEnabled stored for request

    Note over DC: getE2BDocFormat(fileName)
    DC->>FF: isFeatureEnabled('mothership-beta') no userId
    FF->>AC: fetchAppConfigProfile (cached)
    AC-->>FF: FeatureFlagsConfig
    FF-->>DC: false if userIds-only targeting
Loading

Comments Outside Diff (2)

  1. apps/sim/lib/copilot/tools/server/files/doc-compile.ts, line 242-244 (link)

    P2 mothership-beta per-user targeting silently ignored

    getE2BDocFormat calls isFeatureEnabled('mothership-beta') with no user context, so if the AppConfig document is configured with { "enabled": false, "userIds": ["…"] } rather than a global enabled: true, the xlsx gate evaluates as false for every caller — including users who are explicitly in the userIds list. The same omission appears in function-execute.ts (resolveInputFiles) and workflow-alias-resolver.ts, while WorkspaceVfs.materialize already threads userId correctly. If per-user targeting of mothership-beta is ever used, those three paths silently deny beta access to targeted users.

  2. apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx, line 146-154 (link)

    P2 Embedded table always hides workflow columns

    workflowColumnsEnabled defaults to false, so any embedded usage of Table (e.g. the mothership chat panel described in the prop comment) will always render without the workflow column type. Previously NEXT_PUBLIC_WORKFLOW_COLUMNS_ENABLED was baked at build time and applied globally — both the page route and embedded usage respected it equally. Now only the page route gets the server-resolved flag; the embedding component would need to fetch and thread the flag value itself, which isn't wired up.

    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!

Reviews (1): Last reviewed commit: "feat(feature-flags): migrate 3 env-flags..." | Re-trigger Greptile

@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates three startup-time env-gate flags (isTablesFractionalOrderingEnabled, isMothershipBetaFeaturesEnabled, isWorkflowColumnsEnabledClient) to AppConfig-backed runtime flags via isFeatureEnabled, and fixes the existing feature-flags.ts schema to match the actual AppConfig document format (flat top-level keys, adminEnabled not admins).

  • workflow-columns moves from a NEXT_PUBLIC_* build-time bake to server-side resolution in TablePage, threaded as a prop through TableTableGridNewColumnDropdown; however the embedded <Table> in the mothership resource-content view is not updated and will always evaluate the flag as false.
  • mothership-beta is resolved per-user in WorkspaceVFS.materialize (the one place where userId is in scope) and cached on the VFS instance; other callers (function-execute.ts, workflow-alias-resolver.ts, getE2BDocFormat) call the flag without a user context.
  • tables-fractional-ordering is distributed across each async call site in rows/ordering.ts and rows/service.ts; buildRowOrderBySql correctly converted to accept the flag as a synchronous boolean parameter.

Confidence Score: 3/5

The core flag migration and schema fix are correct, but the embedded-table path is a behavioral regression that would silently suppress workflow columns for any user who has the flag enabled.

The embedded <Table> in resource-content.tsx never receives workflowColumnsEnabled, so it permanently renders with workflow columns disabled regardless of the AppConfig value. Before this PR the build-time NEXT_PUBLIC_* variable applied universally; now only the dedicated TablePage gets the flag resolved. Any deployment that turns on workflow-columns would see an inconsistency between the main table page and the mothership embedded view.

resource-content.tsx needs workflowColumnsEnabled threaded from a server component or resolved via a lightweight server action/hook; ordering.ts has redundant independent flag fetches inside insertOrderedRow worth reviewing for correctness during flag rollout.

Important Files Changed

Filename Overview
apps/sim/lib/core/config/feature-flags.ts Schema fix (no flags wrapper, adminEnabled instead of admins), 3 new flags registered; logic is correct and well-structured.
apps/sim/lib/core/config/env-flags.ts Three flags removed cleanly; remaining env-flags are unaffected.
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/page.tsx Page converted to async server component, resolves workflow-columns flag with user session and passes it as prop; correct pattern.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx Embedded <Table> usage doesn't pass workflowColumnsEnabled, permanently disabling workflow columns for all embedded-table renders even when the flag is enabled for the user.
apps/sim/lib/table/rows/ordering.ts Flag migrated correctly but insertOrderedRow and its inner helpers each independently fetch the same flag, creating a theoretical inconsistency window during flag transitions.
apps/sim/lib/table/rows/service.ts buildRowOrderBySql correctly converted to accept a fractionalOrderingEnabled boolean parameter; queryRows and findRowMatches fetch the flag and thread it through cleanly.
apps/sim/lib/copilot/vfs/workspace-vfs.ts Beta flag resolved once at materialize time with userId and cached as _betaEnabled; good pattern for per-user resolution within a VFS lifecycle. getE2BDocFormat callers correctly awaited.
apps/sim/lib/copilot/tools/server/files/doc-compile.ts getE2BDocFormat correctly made async; all callers updated. xlsx gate uses mothership-beta without userId context, which is consistent with other non-VFS callers.
apps/sim/lib/copilot/tools/handlers/function-execute.ts Beta flag evaluation moved from env-flag to isFeatureEnabled but called without userId, meaning per-user allowlisting won't work from this path.
apps/sim/lib/table/tests/lock-order.test.ts Mock updated from env-flag to isFeatureEnabled returning resolved false; correct approach.
apps/sim/lib/table/tests/update-row.test.ts Same mock update as lock-order test; correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant TablePage as TablePage (Server)
    participant AppConfig
    participant Table as Table (Client)
    participant TableGrid
    participant NewColDrop as NewColumnDropdown
    participant ResourceContent as resource-content (Client)

    TablePage->>AppConfig: "isFeatureEnabled('workflow-columns', {userId})"
    AppConfig-->>TablePage: boolean
    TablePage->>Table: "workflowColumnsEnabled={boolean}"
    Table->>TableGrid: "workflowColumnsEnabled={boolean}"
    TableGrid->>NewColDrop: "workflowColumnsEnabled={boolean}"

    Note over ResourceContent: Embedded table path (regression)
    ResourceContent->>Table: workspaceId, tableId, embedded
    Note over Table: workflowColumnsEnabled defaults to false
    Table->>TableGrid: "workflowColumnsEnabled=false"
    TableGrid->>NewColDrop: "workflowColumnsEnabled=false"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant TablePage as TablePage (Server)
    participant AppConfig
    participant Table as Table (Client)
    participant TableGrid
    participant NewColDrop as NewColumnDropdown
    participant ResourceContent as resource-content (Client)

    TablePage->>AppConfig: "isFeatureEnabled('workflow-columns', {userId})"
    AppConfig-->>TablePage: boolean
    TablePage->>Table: "workflowColumnsEnabled={boolean}"
    Table->>TableGrid: "workflowColumnsEnabled={boolean}"
    TableGrid->>NewColDrop: "workflowColumnsEnabled={boolean}"

    Note over ResourceContent: Embedded table path (regression)
    ResourceContent->>Table: workspaceId, tableId, embedded
    Note over Table: workflowColumnsEnabled defaults to false
    Table->>TableGrid: "workflowColumnsEnabled=false"
    TableGrid->>NewColDrop: "workflowColumnsEnabled=false"
Loading

Reviews (2): Last reviewed commit: "feat(feature-flags): migrate 3 env-flags..." | Re-trigger Greptile

Comment thread apps/sim/lib/table/rows/ordering.ts
): Promise<SandboxFile[]> {
const sandboxFiles: SandboxFile[] = []
let totalSize = 0
const betaEnabled = await isFeatureEnabled('mothership-beta')

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.

P2 mothership-beta evaluated without user context in several callers

isFeatureEnabled('mothership-beta') is called here (and in workflow-alias-resolver.ts and getE2BDocFormat in doc-compile.ts) without a userId. If the flag is later configured with a userIds allowlist in AppConfig, beta-enabled users would be denied access via these code paths even though WorkspaceVFS.materialize would correctly grant it. The per-user semantics promised in WorkspaceVFS are silently absent anywhere the call chain doesn't have a userId available — worth documenting or passing through as a future hardening item.

@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 and found 2 potential issues.

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 4745d51. Configure here.

Comment thread apps/sim/lib/copilot/tools/handlers/function-execute.ts
@TheodoreSpeaks TheodoreSpeaks merged commit 06f1e72 into staging Jun 16, 2026
16 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the feat/migrate-ff branch June 16, 2026 03:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant