Skip to content

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

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

feat(blocks): add block visibility gating (preview blocks + AppConfig reveals)#5526
TheodoreSpeaks wants to merge 4 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.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 9, 2026 2:49am

Request Review

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Broad touch surface (registry, copilot discovery, permissions UI) with careful invariants documented, but mis-wiring visibility on a new consumer could hide blocks incorrectly or leak preview metadata; execution paths are explicitly kept ungated.

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

Model: preview: true on BlockConfig (fail-closed everywhere until revealed), hosted block-visibility AppConfig rules (enabled, org/user allowlists, adminEnabled), and PREVIEW_BLOCKS env for self-host/dev. getBlock() stays pure; getAllBlocks() / getCanonicalBlocksByCategory() clone-not-remove gated types with hideFromToolbar: true and append " (Preview)" when appropriate. Shared predicate: isHiddenUnder in blocks/visibility/.

Client: GET /api/blocks/visibility, React Query hook, BlockVisibilityLoader in workspace layout, client hydrate + overlay version bumps (shared with custom blocks) so toolbar, mentions, search index, and integration matcher refresh.

Copilot: Per-viewer filtering on VFS stamp, exposed integration tools, tool-schema LRU (visibility signature), get_blocks_metadata / trigger discovery via withBlockVisibility ALS; process-global caches stay ungated.

Other: appconfig-rules.ts extracted (feature-flags refactored); Access Control keeps full allowlist universe but hides gated types in the UI; docs/registry checks skip preview blocks; /gate-block skill; incidental xlsx dependency swap.

Reviewed by Cursor Bugbot for commit d4b0b0b. 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.

# Conflicts:
#	scripts/check-api-validation-contracts.ts
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