Skip to content

fix(copilot): don't surface custom blocks whose definition was deleted#5498

Merged
TheodoreSpeaks merged 4 commits into
stagingfrom
fix/copilot-vfs-stale-custom-block
Jul 8, 2026
Merged

fix(copilot): don't surface custom blocks whose definition was deleted#5498
TheodoreSpeaks merged 4 commits into
stagingfrom
fix/copilot-vfs-stale-custom-block

Conversation

@TheodoreSpeaks

@TheodoreSpeaks TheodoreSpeaks commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • The copilot kept seeing a custom block after its definition was deleted. Two leaks, both fixed:
  • Placed instances (the reported bug): a workflow's state.json/lint.json come from the raw normalized blocks, so a custom block placed in a workflow survived deletion of its definition and the copilot still read it. loadNormalized now drops any placed custom_block_* whose definition no longer exists (plus edges touching it), keyed off the workspace's current definitions. A live definition — enabled or disabled — is kept; only a deleted one is removed.
  • Static component cache: getStaticComponentFiles is per-process and built from the overlay-aware getAllBlocks(), so a first build inside a custom-block overlay could freeze that org's blocks in and let a later-deleted definition linger. Custom blocks are now excluded from the static cache — they're per-org/dynamic and materialized fresh every request.

Type of Change

  • Bug fix

Testing

Deleted a published custom block still placed in a workflow → the copilot no longer resolves or reads it (state.json omits the block + its edges). bun run lint and check:api-validation:strict pass; existing VFS tests pass (41).

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 Jul 7, 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 8, 2026 12:39am

Request Review

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Copilot read-path filtering only; fails open when custom block definitions fail to load, and does not change persisted workflow data.

Overview
Fixes copilot still seeing placed custom blocks after their deploy-as-block definition was removed. Workflow state.json and lint.json were built from raw normalized tables, so orphaned custom_block_* instances (and connected edges) stayed visible even when the definition no longer existed.

Workspace VFS now tracks the workspace’s current custom block types (enabled and disabled) during materializeCustomBlocks, resets that set on each materialize, and runs dropDeletedCustomBlocks on every memoized loadNormalized result. Blocks whose type is custom but not in that set are removed, along with edges touching them. If definitions never loaded or the load failed (_customBlockTypes === null), nothing is stripped so a transient error cannot wipe all custom blocks from copilot view.

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

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a stale-cache bug in the copilot VFS where a deleted custom block's JSON definition would persist in the per-process memoized static component file map. The root cause was that getStaticComponentFiles() called getAllBlocks() (which is overlay-aware), and if the memoized build first ran inside a custom-block overlay, that org's blocks got frozen into the static cache for the lifetime of the process — surviving even after the block was deleted.

  • One-line filter fix: isCustomBlockType(b.type) is added to the visibleBlocks filter in getStaticComponentFiles(), ensuring custom blocks are never written into the frozen per-process cache.
  • Ordering hazard closed: Static files are merged into this.files after materializeCustomBlocks runs, so a stale static entry for a deleted custom block would have overwritten the correctly-empty per-request result; removing custom blocks from the static cache eliminates this overwrite path entirely.
  • Dynamic path unaffected: materializeCustomBlocks continues to query the DB on every request and only adds enabled custom blocks, so deletions take effect immediately on the next materialization.

Confidence Score: 5/5

Safe to merge — the change is a single targeted filter addition that removes dynamic, per-org data from a frozen per-process cache without touching any first-party block paths.

The fix correctly addresses both sides of the bug: custom blocks no longer enter the static cache (preventing stale entries from being frozen), and because static files are applied after materializeCustomBlocks, the overwrite path that let stale entries shadow the fresh per-request result is also closed. The isCustomBlockType helper is already well-tested and shared across the codebase. No first-party block behavior is affected, and the per-request custom block materialization path is unchanged.

No files require special attention. The single changed line in workspace-vfs.ts is self-contained and the imported isCustomBlockType predicate already exists in blocks/custom/build-config.ts.

Important Files Changed

Filename Overview
apps/sim/lib/copilot/vfs/workspace-vfs.ts Adds isCustomBlockType filter to getStaticComponentFiles() so custom blocks are never frozen into the per-process memoized cache; they continue to be materialized fresh per-request by materializeCustomBlocks.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Req as Request (per-call)
    participant VFS as WorkspaceVFS
    participant Static as staticComponentFiles (per-process, memoized)
    participant DB as Database

    Note over Static: Built once on first call. Before fix: may contain deleted custom block entries if overlay was active during first build.

    Req->>VFS: materialize(workspaceId)
    VFS->>DB: listCustomBlocksWithInputs(workspaceId)
    DB-->>VFS: [only enabled/active blocks]
    VFS->>VFS: materializeCustomBlocks() writes fresh entries to this.files

    VFS->>Static: getStaticComponentFiles()
    Note over Static: After fix: custom_block_* types filtered out during build. Only first-party blocks cached.
    Static-->>VFS: first-party blocks only

    VFS->>VFS: merge static to this.files (no custom block entries to overwrite the per-request materialization)
    VFS-->>Req: VFS with correct custom blocks
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 Req as Request (per-call)
    participant VFS as WorkspaceVFS
    participant Static as staticComponentFiles (per-process, memoized)
    participant DB as Database

    Note over Static: Built once on first call. Before fix: may contain deleted custom block entries if overlay was active during first build.

    Req->>VFS: materialize(workspaceId)
    VFS->>DB: listCustomBlocksWithInputs(workspaceId)
    DB-->>VFS: [only enabled/active blocks]
    VFS->>VFS: materializeCustomBlocks() writes fresh entries to this.files

    VFS->>Static: getStaticComponentFiles()
    Note over Static: After fix: custom_block_* types filtered out during build. Only first-party blocks cached.
    Static-->>VFS: first-party blocks only

    VFS->>VFS: merge static to this.files (no custom block entries to overwrite the per-request materialization)
    VFS-->>Req: VFS with correct custom blocks
Loading

Reviews (1): Last reviewed commit: "fix(copilot): keep custom blocks out of ..." | Re-trigger Greptile

… state so the copilot can't see unrenderable blocks
@TheodoreSpeaks TheodoreSpeaks changed the title fix(copilot): keep deleted custom-block definitions out of the VFS static cache fix(copilot): stop surfacing deleted custom-block definitions (placed instances + static cache) Jul 8, 2026

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

Comment thread apps/sim/lib/copilot/vfs/workspace-vfs.ts
@TheodoreSpeaks TheodoreSpeaks changed the title fix(copilot): stop surfacing deleted custom-block definitions (placed instances + static cache) fix(copilot): don't surface custom blocks whose definition was deleted Jul 8, 2026
The VFS static cache is only ever built by materialize(), which runs
outside any custom-block overlay (the overlay is scoped to edit_workflow
/ get_blocks_metadata, neither of which materializes the VFS). So
getAllBlocks() there always returns first-party blocks only and no
custom block can be frozen into the cache. Removed the dead guard.
@TheodoreSpeaks TheodoreSpeaks merged commit aad2544 into staging Jul 8, 2026
17 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the fix/copilot-vfs-stale-custom-block branch July 8, 2026 00:51
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