Skip to content

fix(copilot): mint ids for nested blocks instead of storing the model's handle - #6626

Open
waleedlatif1 wants to merge 2 commits into
stagingfrom
investigate/workflow-blocks-insert
Open

fix(copilot): mint ids for nested blocks instead of storing the model's handle#6626
waleedlatif1 wants to merge 2 commits into
stagingfrom
investigate/workflow-blocks-insert

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • workflow_blocks.id is a global primary key, but normalizeBlockIdsInOperations only minted UUIDs for an operation's own block_id. Children arrive keyed by the model's handle under params.nestedNodes and were persisted verbatim, so a workflow adding a child named waitPoll collided with whichever workflow stored that name first and the entire save failed with 23505.
  • The pre-insert delete is scoped to the workflow's own rows, so it can't clear the conflicting row — every retry fails identically. The editor path always mints UUIDs client-side, which is why only copilot hit this.
  • Claim non-UUID nestedNodes keys recursively at any container depth, and rewrite child connections and nested containers through the same mapping.
  • Log the Postgres cause on a failed save. Drizzle's message is only Failed query: <sql> params: <...>; the SQLSTATE and constraint live on error.cause, which is why this took a week to diagnose.

Root-caused from production CloudWatch: 38 failures today across 3 workflows, and the same signature on Aug 5, 8 and 11 — this is not new. Confirmed against prod: 613 name-like ids across 244 workflows are already stored, each permanently squatting that name (waitPoll since Jun 10, gmailSend since Jul 9, buildLead since Aug 5). Reproduced against Postgres 17.9: error.cause.code = 23505, detail = Key (id)=(emailWriter) already exists.

Type of Change

  • Bug fix

Testing

3 regression tests added. Verified they fail without the fix (reverted it and watched them go red), then pass with it. Full suite green: 234 tests across the edit-workflow and persistence suites, type-check clean.

Notes / follow-ups

Deliberately scoped. Three things this does not fix:

  • edit ops also create children via mergeNestedNodesForParent. Widening the gate would regress: a child matching an existing block by name keeps that block's id, so claiming its handle would repoint sibling references at an id no block was created under. That path needs the id minted at creation with an alias — documented in the TSDoc.
  • apps/realtime/src/database/operations.ts:1004 upserts with onConflictDoUpdate({ target: workflowBlocks.id }) unscoped by workflow, so a colliding id silently overwrites another workflow's block rather than erroring. Separate bug, worth its own fix.
  • The structural fix is a composite PK on (workflow_id, id) — only two FK dependents (workflow_edges.source_block_id/target_block_id), both already carrying an indexed workflow_id. That would also close the realtime clobber and make the 613 squatters permanently benign.

There is also a second, distinct failure mode on this path affecting all-UUID payloads that this doesn't address; the error.cause logging is what will identify it.

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)

…'s handle

workflow_blocks.id is a global primary key, but normalizeBlockIdsInOperations
only minted UUIDs for an operation's own block_id. Children arrive keyed by the
model's handle under params.nestedNodes and were persisted verbatim, so a
workflow adding a child named "waitPoll" collided with whichever workflow stored
that name first and the whole save failed with 23505. The pre-insert delete is
scoped to the workflow's own rows, so it cannot clear the conflicting row and
every retry fails identically.

- claim non-UUID nestedNodes keys recursively, at any container depth
- rewrite child connections and nested containers through the same mapping
- log the Postgres cause on a failed save; Drizzle's message is only
  "Failed query: <sql> params: <...>" and the SQLSTATE lives on error.cause
@vercel

vercel Bot commented Aug 12, 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 Aug 12, 2026 4:47pm

Request Review

@cursor

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches copilot workflow persistence and global block id assignment; behavior change is targeted to non-UUID nested ids but incorrect remapping could break nested graphs.

Overview
Copilot edit-workflow saves were failing with Postgres 23505 when the model added nested children under params.nestedNodes using human-readable keys (e.g. waitPoll), because only top-level block_id values were normalized to UUIDs while workflow_blocks.id is global.

normalizeBlockIdsInOperations now claims non-UUID ids recursively for add / insert_into_subflow (including nested containers), then rewrites nested node keys, nested connections, and top-level connections through the same mapping; duplicate handles in one batch log a warning. Regression tests cover flat nested children, deep nesting with sibling references, and unchanged existing UUIDs.

Failed normalized-table saves also log describeError and getPostgresConstraintName on the error cause so SQLSTATE/constraint show up in logs.

Reviewed by Cursor Bugbot for commit 7894c0b. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR recursively replaces model-generated nested block handles with UUIDs before workflow persistence and rewrites nested connection references through the same mapping. It also logs underlying PostgreSQL error details when normalized workflow persistence fails.

  • Adds two-pass recursive ID claiming and reference normalization for nested workflow blocks.
  • Warns when duplicate handles in an edit batch resolve to the same identifier.
  • Adds regression coverage for nested containers, sibling references, and existing UUIDs.
  • Adds PostgreSQL cause and constraint diagnostics to persistence failures.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/builders.ts Recursively claims nested block handles, normalizes nested references, and emits the duplicate-handle warning requested in the prior review.
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/builders.test.ts Adds focused regression tests for UUID minting, recursive containers, sibling connections, and already-valid UUIDs.
apps/sim/lib/workflows/persistence/utils.ts Extends failed-save logging with the underlying error description and PostgreSQL constraint name.

Reviews (2): Last reviewed commit: "fix(copilot): warn when an edit batch re..." | Re-trigger Greptile

Comment thread apps/sim/lib/copilot/tools/server/workflow/edit-workflow/builders.ts Outdated
Two declarations sharing a handle collapse to a single block, because
references naming that handle are ambiguous and the flat id mapping has no
way to express a per-declaration-site id. Behavior is unchanged; the warn
makes the case visible in production instead of silent.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 7894c0b. Configure here.

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