Skip to content

feat(store): add Upsert() to BuiltinToolStore for additive tool seeding#554

Open
duhd-vnpay wants to merge 1 commit intonextlevelbuilder:mainfrom
duhd-vnpay:feat/builtin-tool-upsert
Open

feat(store): add Upsert() to BuiltinToolStore for additive tool seeding#554
duhd-vnpay wants to merge 1 commit intonextlevelbuilder:mainfrom
duhd-vnpay:feat/builtin-tool-upsert

Conversation

@duhd-vnpay
Copy link
Copy Markdown

Fixes #336

Problem

BuiltinToolStore.Seed() performs a reconcile DELETE that removes all rows not in the provided list:

DELETE FROM builtin_tools WHERE name != ALL($1)

When a fork calls Seed() twice — once for 28 upstream tools, then again for 2 fork-local tools — the second call's DELETE wipes all 28 upstream tools. Only the 2 fork tools remain visible.

Fix

Add Upsert() method that performs the same INSERT ON CONFLICT DO UPDATE as Seed() but skips the reconcile DELETE. Fork-specific seed functions use Upsert() to additively register tools.

// Seed: full reconciliation — for upstream canonical tool set
Seed(ctx, upstreamTools)   // INSERT 28 tools, DELETE stale

// Upsert: additive — for fork-specific tools
Upsert(ctx, forkTools)     // INSERT 2 tools, no DELETE

Behavior comparison

Method INSERT ON CONFLICT Preserve user enabled/settings Reconcile DELETE
Seed() ✅ (removes stale rows)
Upsert() ❌ (additive only)

Both methods preserve user-customized enabled and settings values — the ON CONFLICT clause only overwrites settings when the existing value is {} or null.

Changes

File Lines Change
internal/store/builtin_tool_store.go +3 Add Upsert() to interface
internal/store/pg/builtin_tools.go +51 PostgreSQL implementation
internal/store/sqlitestore/builtin-tools.go +51 SQLite implementation

Verification

go build ./...                   # ✅ PG build
go build -tags sqliteonly ./...  # ✅ SQLite build  
go vet ./...                     # ✅ Clean

🤖 Generated with Claude Code

Seed() performs a reconcile DELETE that removes all tools not in the
provided list. This breaks fork workflows where Seed() is called twice:
once for upstream tools, once for fork-local additions — the second call
wipes all upstream tools.

Add Upsert() that performs the same INSERT ON CONFLICT DO UPDATE but
skips the reconcile DELETE. Fork-specific seed functions can use Upsert()
to additively register tools without affecting the upstream set.

Implemented for both PostgreSQL and SQLite backends.

Fixes nextlevelbuilder#336

Co-Authored-By: Claude Opus 4.6 (1M context) <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.

feat: add Upsert() to BuiltinToolStore to support fork-specific tool seeding without reconcile DELETE

1 participant