From 9d90308e2063377f3f0a8dcfa949a5277bb980df Mon Sep 17 00:00:00 2001 From: xarmian Date: Thu, 9 Apr 2026 14:49:03 +0000 Subject: [PATCH] fix: resolve plan view crash from duplicate children and tiptap link conflict Three fixes: 1. GetChildItems returned duplicate rows when an item was linked to a parent via multiple link types (e.g. both "parent" and "implements"), causing Svelte's {#each} to throw each_key_duplicate. Added SELECT DISTINCT. 2. StarterKit v3.20.4 now includes Link by default, conflicting with our custom SafeLink extension. Disabled StarterKit's built-in link. 3. Migration runner now tolerates "duplicate column name" errors on ALTER TABLE ADD COLUMN, making migrations idempotent when partially applied (e.g. server crash mid-migration). --- internal/store/items.go | 2 +- internal/store/store.go | 10 +++++++++- web/src/lib/components/editor/Editor.svelte | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/store/items.go b/internal/store/items.go index fd378c7e..50741d59 100644 --- a/internal/store/items.go +++ b/internal/store/items.go @@ -1317,7 +1317,7 @@ func (s *Store) GetAllItemProgress(workspaceID, collectionSlug string) ([]ItemPr // via item_links. Returns children from any collection. func (s *Store) GetChildItems(parentItemID string) ([]models.Item, error) { rows, err := s.db.Query(s.q(fmt.Sprintf(` - SELECT i.id, i.workspace_id, i.collection_id, i.title, i.slug, i.content, i.fields, i.tags, + SELECT DISTINCT i.id, i.workspace_id, i.collection_id, i.title, i.slug, i.content, i.fields, i.tags, i.pinned, i.sort_order, i.parent_id, i.assigned_user_id, i.agent_role_id, i.role_sort_order, i.created_by, i.last_modified_by, i.source, i.item_number, i.created_at, i.updated_at, diff --git a/internal/store/store.go b/internal/store/store.go index a5f0631b..d6182bb7 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -255,7 +255,15 @@ func execMulti(db *sql.DB, sqlText string) error { stmt := strings.TrimSpace(sqlText[:end+1]) if stmt != "" && stmt != ";" { if _, err := db.Exec(stmt); err != nil { - return fmt.Errorf("exec migration statement: %w\nStatement: %.200s", err, stmt) + // Tolerate "duplicate column name" errors from ALTER TABLE ADD COLUMN. + // This makes migrations idempotent when partially applied (e.g. server + // crashed after adding a column but before recording the migration). + upper := strings.ToUpper(strings.TrimSpace(stmt)) + isDupCol := strings.Contains(err.Error(), "duplicate column name") + isAddCol := strings.HasPrefix(upper, "ALTER TABLE") && strings.Contains(upper, "ADD COLUMN") + if !(isDupCol && isAddCol) { + return fmt.Errorf("exec migration statement: %w\nStatement: %.200s", err, stmt) + } } } sqlText = sqlText[end+1:] diff --git a/web/src/lib/components/editor/Editor.svelte b/web/src/lib/components/editor/Editor.svelte index 817aa18f..83d82762 100644 --- a/web/src/lib/components/editor/Editor.svelte +++ b/web/src/lib/components/editor/Editor.svelte @@ -230,6 +230,7 @@ const extensions = [ StarterKit.configure({ codeBlock: false, + link: false, // We use our own SafeLink extension below }), MermaidCodeBlock.configure({ HTMLAttributes: { class: 'code-block' },