Skip to content

feat(blocks): add block visibility gating (preview blocks + AppConfig reveals)#5526

Open
TheodoreSpeaks wants to merge 3 commits into
stagingfrom
feat/block-ff
Open

feat(blocks): add block visibility gating (preview blocks + AppConfig reveals)#5526
TheodoreSpeaks wants to merge 3 commits into
stagingfrom
feat/block-ff

Conversation

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator

Summary

  • preview: true on a BlockConfig hides the block from every discovery surface (toolbar, cmd+K, mentions, tool picker, mothership VFS/metadata/tools, Access Control, docs/catalog) in every environment until revealed — fail-closed, including SSR
  • reveal paths: hosted block-visibility AppConfig document (per-block {enabled, orgIds, userIds, adminEnabled} rules — admins/org allowlists get a (Preview) name suffix, enabled: true = GA) or PREVIEW_BLOCKS env for self-host/dev; enabled: false on a shipped block = discovery kill switch
  • execution is never gated: getBlock stays pure, gated blocks stay in getAllBlocks() as clones (clone-not-remove), placed instances keep running
  • new blocks/visibility/ context (client resolver + per-request server ALS, independent of the custom-block overlay), registry projection with a zero-cost fast path when no preview blocks exist
  • shared AppConfig rule parsing extracted to appconfig-rules.ts (feature-flags refactored onto it)
  • mothership: VFS static files + exposed integration tools stay ungated process-global caches, filtered per-viewer at stamp/consumer time; tool-schema LRU keyed by visibility signature; visibility memoized per (user, workspace)
  • generate-docs/check-block-registry skip preview blocks (no public docs/catalog/meta until GA); Access Control hides them from the list while keeping stored allowlists viewer-independent
  • /gate-block skill documents the lifecycle; infra profile landed in simstudioai/infra#262

Type of Change

  • New feature

Testing

966 tests across touched suites (new coverage: rule parsing, visibility evaluation, registry projection, hydrate no-op, route, preview-not-exempt), tsc, lint, check:api-validation:strict, react-query + client-boundary audits, check-block-registry — all green. Route-count baseline 919→920 (new contract-bound route).

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)

The dependency was pinned to a direct tarball on cdn.sheetjs.com, which
now returns 403 (Cloudflare bot-challenge) to automated clients, breaking
bun install in CI. npm's own xlsx is frozen at 0.18.5, so switch to the
@e965/xlsx mirror which republishes the identical 0.20.3 CDN build to the
npm registry. No code changes needed — all imports use bare 'xlsx'.
@TheodoreSpeaks TheodoreSpeaks requested a review from a team as a code owner July 9, 2026 01:19
@vercel

vercel Bot commented Jul 9, 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 Jul 9, 2026 1:31am

Request Review

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Wide blast radius across registry, copilot VFS/tools, and access-control UI, but execution is explicitly excluded and the change is heavily tested with documented invariants.

Overview
Adds block visibility gating so unreleased blocks can ship in code but stay off discovery surfaces until revealed, and shipped blocks can be kill-switched from discovery without stopping runs.

preview: true on BlockConfig hides a block everywhere (toolbar, search, mentions, copilot/VFS, access-control UI, docs) until block-visibility AppConfig rules or PREVIEW_BLOCKS env reveal it; partial reveals get a " (Preview)" name suffix. getBlock stays pure — execution and placed instances are unchanged; gated types remain in getAllBlocks() as clones with hideFromToolbar: true.

New blocks/visibility/ layer (client hydrate + server AsyncLocalStorage), GET /api/blocks/visibility, and BlockVisibilityLoader drive registry projection and invalidate toolbar/search/matcher caches via the shared overlay version. Copilot filters integration tools and VFS static files per viewer while keeping process-global caches ungated. Shared appconfig-rules parsing is extracted (feature-flags refactored onto it). Docs/registry checks and a /gate-block skill document the lifecycle.

Also switches the xlsx dependency to npm:@e965/xlsx@0.20.3.

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

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds block visibility gating for preview and kill-switched blocks. The main changes are:

  • New AppConfig and env-based block visibility rules.
  • Registry projections that hide gated blocks from discovery surfaces.
  • Workspace client hydration for viewer-specific visibility.
  • Copilot, VFS, Access Control, docs, and catalog filtering updates.
  • Shared AppConfig rule parsing reused by feature flags.

Confidence Score: 4/5

This is close, but I would fix the copilot edit bypass before merging.

  • Most discovery surfaces now use the new visibility state.
  • The copilot edit path can still place shipped blocks that are hidden by AppConfig.
  • The fix likely needs visibility state around the edit workflow path without writing display-only projections into persisted workflow data.

apps/sim/lib/copilot/tools/server/router.ts

Important Files Changed

Filename Overview
apps/sim/lib/core/config/block-visibility.ts Adds hosted and env-based visibility evaluation for preview reveals and shipped-block kill switches.
apps/sim/blocks/registry.ts Projects registry lists through visibility state while keeping direct block lookup unchanged for execution.
apps/sim/lib/copilot/tools/server/router.ts Scopes selected copilot tools with visibility state, but leaves workflow editing outside that scope.
apps/sim/app/workspace/[workspaceId]/providers/block-visibility-loader.tsx Hydrates per-workspace visibility state on the client and resets it during workspace switches.

Reviews (3): Last reviewed commit: "fix(blocks): reset visibility to fail-cl..." | Re-trigger Greptile

Comment thread apps/sim/lib/core/config/block-visibility.ts

@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 e6ad905. Configure here.

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

Comment thread apps/sim/lib/core/config/block-visibility.ts
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile review

* (find-by-type over clones, never a discovery listing) and gating it would
* only risk leaking display projections into persisted state.
*/
const VISIBILITY_GATED_TOOLS = new Set(['get_blocks_metadata', 'get_trigger_blocks'])

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 Edit path bypasses gating

edit_workflow runs outside withBlockVisibility, so registry reads in that tool see no active visibility state. That still hides unrevealed preview blocks, but it does not apply the disabled set for shipped blocks. When AppConfig kill-switches a shipped block with enabled: false, the filtered copilot discovery tools hide it, but the edit tool can still resolve and place it in a workflow. Existing placed blocks can keep executing, but this path still allows new placements through the agent edit flow.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deliberate, and documented as the placeable-by-id contract (see the /gate-block skill invariants): gating is discovery-only, and every add-by-explicit-id path behaves identically — workflow JSON import, cross-workspace duplicate, the admin API, and the agent edit flow when the user explicitly names the type. The agent cannot discover a gated block (metadata/VFS/tool listing are all filtered), so this path only fires when a user asks for the block by name — same trust model as import. Wrapping edit_workflow in withBlockVisibility would not change this anyway: its type validation resolves via pure getBlock (never gated) by design, because that same resolution path is what keeps already-placed gated blocks rendering and executing. Blocking placement would require a semantic change to the kill switch (discovery-only → placement enforcement) that we explicitly scoped out.

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