Skip to content

Comments

fix: use table recreation for sub_agent_skills unique constraint migration#2208

Closed
amikofalvy wants to merge 1 commit intomainfrom
fix/doltgres-sub-agent-skills-constraint
Closed

fix: use table recreation for sub_agent_skills unique constraint migration#2208
amikofalvy wants to merge 1 commit intomainfrom
fix/doltgres-sub-agent-skills-constraint

Conversation

@amikofalvy
Copy link
Collaborator

Summary

  • Fixes pnpm db:migrate failure caused by Doltgres's inability to drop inline UNIQUE constraints via standard PostgreSQL syntax
  • Replaces the DROP CONSTRAINT / ADD CONSTRAINT approach in migration 0010_oval_angel.sql with a full table recreation (create new table → copy data → drop old → rename) to work around the Doltgres limitation
  • The unique constraint on sub_agent_skills is widened from (sub_agent_id, skill_id) to (tenant_id, project_id, agent_id, sub_agent_id, skill_id) to properly scope it to tenant/project/agent

Context

Doltgres stores inline UNIQUE constraints from CREATE TABLE as internal keys rather than PostgreSQL-style constraints. This makes them impossible to drop through any standard syntax:

Approach Result
ALTER TABLE ... DROP CONSTRAINT "Constraint does not exist"
ALTER TABLE ... DROP CONSTRAINT IF EXISTS No-op, then ADD CONSTRAINT fails with "Duplicate key name"
DROP INDEX IF EXISTS (standalone) "table not found"
ALTER TABLE ... DROP INDEX (MySQL syntax) Syntax error (PG wire protocol rejects it)

The table recreation approach sidesteps this entirely by creating a fresh table with the correct constraint.

Test plan

  • Run pnpm db:migrate locally and verify it completes successfully
  • Verify sub_agent_skills table has the correct unique constraint after migration
  • Run pnpm check to ensure no regressions

Made with Cursor

…ation

Doltgres stores inline UNIQUE constraints as internal keys rather than
PostgreSQL-style constraints, making them impossible to drop via standard
DROP CONSTRAINT or DROP INDEX syntax. Replace the DROP/ADD CONSTRAINT
approach with a full table recreation to work around this Doltgres limitation.

Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot
Copy link

changeset-bot bot commented Feb 20, 2026

⚠️ No Changeset found

Latest commit: 16ad9c4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Feb 20, 2026

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

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Feb 20, 2026 2:57am
agents-docs Ready Ready Preview, Comment Feb 20, 2026 2:57am
agents-manage-ui Ready Ready Preview, Comment Feb 20, 2026 2:57am

Request Review

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(1) Total Issues | Risk: High

🔴❗ Critical (1) ❗🔴

Inline Comments:

  • 🔴 Critical: 0010_oval_angel.sql:1-20 Editing an already-applied migration file violates AGENTS.md migration workflow

✅ What's Done Well

The table recreation pattern itself is the correct approach for working around Doltgres's inability to drop inline UNIQUE constraints. The implementation correctly:

  • Creates a new table with the expanded unique constraint scope
  • Preserves all data via INSERT INTO ... SELECT * FROM
  • Recreates foreign keys and indexes after the rename
  • Matches the Drizzle schema definition in manage-schema.ts

💭 Consider

💭 1) scope Add a comment documenting the Doltgres limitation

Issue: Future maintainers may not understand why the table recreation pattern was used instead of standard ALTER TABLE constraint modification.

Why: The PR description documents this well, but that context will be lost once the PR is merged. A SQL comment in the migration file explaining the Doltgres limitation would help future maintainers.

Fix: Add a comment at the top of the migration:

-- Doltgres stores inline UNIQUE constraints as internal keys rather than
-- PostgreSQL-style constraints, making them impossible to drop via standard
-- syntax. This migration uses table recreation as a workaround.

🚫 REQUEST CHANGES

Summary: The table recreation approach is technically correct for the Doltgres workaround, but this PR edits an existing migration file that already exists on main. Per AGENTS.md, existing migrations should never be modified after being applied — instead, create a new migration file (e.g., 0011_fix_sub_agent_skills_constraint.sql) that handles the fix. This ensures databases that already ran migration 0010 will get the correct schema state via the new migration.


Discarded (2)
Location Issue Reason Discarded
meta/0010_snapshot.json Snapshot should reflect constraint columns Not an issue — the snapshot correctly shows the expanded constraint columns and was not modified by this PR
scope Migration should be tested against PGlite Not applicable — PGlite is used for tests but the fix is specifically for Doltgres behavior
Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-breaking-changes 1 0 0 0 1 0 0
pr-review-devops 3 0 0 0 0 0 2
Total 4 0 0 0 1 0 2

Note: Both reviewers identified the same critical issue (editing existing migration), consolidated into single inline comment.

Comment on lines +1 to +20
CREATE TABLE "sub_agent_skills_new" (
"tenant_id" varchar(256) NOT NULL,
"id" varchar(256) NOT NULL,
"project_id" varchar(256) NOT NULL,
"agent_id" varchar(256) NOT NULL,
"sub_agent_id" varchar(256) NOT NULL,
"skill_id" varchar(64) NOT NULL,
"index" numeric DEFAULT 0 NOT NULL,
"always_loaded" boolean DEFAULT false NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "sub_agent_skills_tenant_id_project_id_agent_id_id_pk" PRIMARY KEY("tenant_id","project_id","agent_id","id"),
CONSTRAINT "sub_agent_skills_sub_agent_skill_unique" UNIQUE("tenant_id","project_id","agent_id","sub_agent_id","skill_id")
);--> statement-breakpoint
INSERT INTO "sub_agent_skills_new" SELECT * FROM "sub_agent_skills";--> statement-breakpoint
DROP TABLE "sub_agent_skills";--> statement-breakpoint
ALTER TABLE "sub_agent_skills_new" RENAME TO "sub_agent_skills";--> statement-breakpoint
ALTER TABLE "sub_agent_skills" ADD CONSTRAINT "sub_agent_skills_sub_agent_fk" FOREIGN KEY ("tenant_id","project_id","agent_id","sub_agent_id") REFERENCES "public"."sub_agents"("tenant_id","project_id","agent_id","id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "sub_agent_skills" ADD CONSTRAINT "sub_agent_skills_skill_fk" FOREIGN KEY ("tenant_id","project_id","skill_id") REFERENCES "public"."skills"("tenant_id","project_id","id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "sub_agent_skills_skill_idx" ON "sub_agent_skills" USING btree ("skill_id");
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 CRITICAL: Editing an already-applied migration file

Issue: This PR modifies 0010_oval_angel.sql, an existing migration file that already exists on main (introduced in PR #2190, reverted in #2205, re-reverted in #2207). Per AGENTS.md line 147: "NEVER edit existing migration SQL files after they've been applied — create new migrations instead."

Why: Drizzle tracks applied migrations by filename in drizzle.__drizzle_migrations. When the migration file content changes but the filename stays the same:

  • Databases where 0010 already ran will not re-run the new SQL (migration marked as "applied")
  • Those databases will have inconsistent schema state (either the old constraint columns if it succeeded, or a broken state if the original Doltgres limitation caused it to fail)
  • New databases will get the correct table recreation approach, causing schema drift between environments

Fix: Create a new migration file (e.g., 0011_fix_sub_agent_skills_constraint.sql) that:

  1. Applies the table recreation pattern for databases that need it
  2. Is idempotent (safe to run on databases in any state)

Example approach:

-- 0011_fix_sub_agent_skills_constraint.sql
-- Check if we need to apply the fix (constraint doesn't have all 5 columns)
-- Then apply table recreation pattern

Alternatively, if you're certain migration 0010 has never been successfully applied to any environment (failed everywhere due to Doltgres limitation), document that assumption clearly and consider whether the rollback strategy is acceptable.

Refs:

@github-actions github-actions bot deleted a comment from claude bot Feb 20, 2026
nick-inkeep added a commit that referenced this pull request Feb 20, 2026
…traint migration

The previous migration used DROP CONSTRAINT + ADD CONSTRAINT which fails on
Doltgres (can't drop UNIQUE constraints). PR #2208's approach of creating a
_new table with same constraint names caused PGlite index name collisions.

This uses backup→drop→recreate→restore: copy data to a temp table, drop the
original (freeing all names), recreate with the wider UNIQUE constraint, then
restore data. Tested on both Doltgres v0.55.3 and PGlite with zero data loss.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@amikofalvy amikofalvy marked this pull request as draft February 20, 2026 07:45
@amikofalvy
Copy link
Collaborator Author

amikofalvy commented Feb 20, 2026

Closing in favor of #2214

@amikofalvy amikofalvy closed this Feb 20, 2026
nick-inkeep added a commit that referenced this pull request Feb 20, 2026
…traint migration

The previous migration used DROP CONSTRAINT + ADD CONSTRAINT which fails on
Doltgres (can't drop UNIQUE constraints). PR #2208's approach of creating a
_new table with same constraint names caused PGlite index name collisions.

This uses backup→drop→recreate→restore: copy data to a temp table, drop the
original (freeing all names), recreate with the wider UNIQUE constraint, then
restore data. Tested on both Doltgres v0.55.3 and PGlite with zero data loss.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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