Skip to content

separate conenction for migrations#2831

Merged
akshaydeo merged 1 commit intomainfrom
04-19-separate_conenction_for_migrations
Apr 20, 2026
Merged

separate conenction for migrations#2831
akshaydeo merged 1 commit intomainfrom
04-19-separate_conenction_for_migrations

Conversation

@akshaydeo
Copy link
Copy Markdown
Contributor

@akshaydeo akshaydeo commented Apr 18, 2026

Summary

Fixes a Postgres error (ERROR: cached plan must not change result type (SQLSTATE 0A000)) that occurred when running database migrations on the same connection pool used for regular queries. Migrations are now run on a dedicated, short-lived database connection that is closed after migrations complete, while the main connection pool is created separately afterward.

Changes

  • Migrations are now executed on a separate, dedicated database connection rather than the main connection pool. This prevents prepared statement cache conflicts that arise when schema changes (from migrations) invalidate cached query plans on an existing connection.
  • The migration connection is explicitly closed after migrations finish, regardless of success or failure.
  • A RefreshConnectionPool method has been added to the ConfigStore interface and RDBConfigStore to support refreshing the connection pool when needed.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Run the service against a Postgres instance where migrations need to be applied and verify that the cached plan must not change result type error no longer occurs on startup.

go test ./framework/configstore/...

Breaking changes

  • Yes
  • No

ConfigStore interface now includes RefreshConnectionPool(ctx context.Context) error. Any external implementations of this interface will need to implement this new method.

Related issues

Closes the issue related to ERROR: cached plan must not change result type (SQLSTATE 0A000) during migrations.

Security considerations

No security implications. The migration connection uses the same credentials and SSL configuration as the main pool and is closed immediately after use.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added public APIs to run a single migration against a fresh connection and to refresh the runtime DB connection pool on demand.
  • Refactor

    • Separated startup migration from the runtime DB pool, introduced safer pool swap/refresh with guaranteed connection cleanup and improved error handling.
  • Tests

    • Updated tests and helpers to use the public DB accessor and support the new migration and pool-refresh hooks.

Walkthrough

RDB stores now use a two-stage GORM lifecycle: a throwaway connection runs migrations/version checks and is closed, then a fresh runtime pool is opened, tuned, atomically stored, and used for runtime DB access. RDBConfigStore exposes RunMigration(fn) and RefreshConnectionPool and routes all DB access through s.DB().

Changes

Cohort / File(s) Summary
ConfigStore interface
framework/configstore/store.go
Replaced RunMigration signature to accept fn func(context.Context, *gorm.DB) error; added RefreshConnectionPool(ctx context.Context) error; removed migrator dependency.
RDB core
framework/configstore/rdb.go, framework/configstore/store.go
Replaced RDBConfigStore.db *gorm.DB with db atomic.Pointer[gorm.DB]; added migrateOnFreshFn and refreshPoolFn hooks; added exported RunMigration(ctx, fn) and RefreshConnectionPool(ctx); changed internals to call s.DB() everywhere.
Postgres config store
framework/configstore/postgres.go
Introduced helpers (buildPostgresDSN, openPostresConnection, closeDbConn, applyPostgresPoolTuning); newPostgresConfigStore now opens a throwaway GORM for migrations/version checks, closes it, then opens a tuned runtime pool and stores it.
Postgres log store
framework/logstore/postgres.go
Adopted two-stage lifecycle for log store: open throwaway pool for version/migrations then close; open tuned runtime pool for normal operation; updated error/close handling to use shared helpers.
SQLite store
framework/configstore/sqlite.go
Create RDBConfigStore without embedding *gorm.DB; register runtime DB via s.db.Store(db); migrateOnFreshFn uses s.DB(); refreshPoolFn set to no-op.
Migrations helper
framework/configstore/migrations.go
Added RunSingleMigration(ctx, db, migration) that validates inputs and runs exactly one migration via migrator.
Encryption & prompts
framework/configstore/encryption.go, framework/configstore/prompts.go
Replaced direct s.db usage with s.DB() across queries, transactions, dialect checks, and batch-encryption routines so operations use the current runtime DB handle.
Tests (configstore & logstore)
framework/configstore/*_test.go, framework/logstore/asyncjob_test.go
Test helpers refactored to create RDBConfigStore, register gorm.DB via s.db.Store(db), update migrateOnFreshFn to call s.DB(), add refreshPoolFn no-op in tests, and update test DB accesses to store.DB(); minor asyncjob context-value test change.
Misc (encryption/migrations tests)
framework/configstore/encryption_test.go, framework/configstore/migrations_test.go
Test setups updated to use atomic-stored DB and store.DB() for migrations; added refreshPoolFn no-op.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Builder as newPostgresConfigStore
    participant MigrateDB as MigrationDB (throwaway)
    participant RuntimeDB as RuntimeDB (main pool)
    Caller->>Builder: create store
    Builder->>MigrateDB: open migration GORM (mDb)
    Builder->>MigrateDB: triggerMigrations(ctx, mDb)
    alt migrations fail
        MigrateDB-->>Builder: error
        Builder->>MigrateDB: close mDb
        Builder-->>Caller: return error
    else migrations succeed
        MigrateDB-->>Builder: success
        Builder->>MigrateDB: close mDb
        Builder->>RuntimeDB: open/configure fresh runtime GORM
        Builder->>RuntimeDB: apply pool tuning
        Builder-->>Caller: return initialized store
    end
Loading
sequenceDiagram
    participant Caller
    participant Store as RDBConfigStore
    participant NewPool as NewRuntimeDB
    participant OldPool as OldRuntimeDB
    Caller->>Store: RefreshConnectionPool(ctx)
    Store->>NewPool: open new runtime pool
    alt open fails
        NewPool-->>Store: error
        Store-->>Caller: return error
    else open succeeds
        NewPool-->>Store: new *gorm.DB
        Store->>OldPool: atomic swap (s.db.Swap(new))
        Store->>OldPool: close old pool
        Store-->>Caller: success
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I dug two tunnels, one to test, one to keep,

Ran migrations in safety, then closed the deep,
I opened a pool fresh, tuned every seam,
Swapped out the old, let the new one gleam,
Hop, hop — connections hum in a dream!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title has a typo ('conenction' instead of 'connection') and is too vague. While it references the main change, 'separate connection for migrations' is imprecise about what problem is being solved. Correct the typo and consider a more descriptive title like 'Fix Postgres prepared statement cache error by using separate migration connection' to clarify the issue being addressed.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is comprehensive and well-structured, covering the problem, solution, test instructions, breaking changes, and security considerations against the provided template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 04-19-separate_conenction_for_migrations

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.4)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Contributor Author

akshaydeo commented Apr 18, 2026

@akshaydeo akshaydeo marked this pull request as ready for review April 18, 2026 18:47
@akshaydeo akshaydeo mentioned this pull request Apr 18, 2026
18 tasks
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 18, 2026

Confidence Score: 5/5

Safe to merge — the previously flagged P0/P1 issues (empty RefreshConnectionPool body, migration pool leak on success and on runtime-pool-open failure) are all resolved in this revision.

All remaining findings are P2 style suggestions; the core two-pool fix and atomic swap logic are correct, connection cleanup is handled on every code path in configstore/postgres.go, and the test helpers are properly updated.

framework/logstore/postgres.go — minor close-error discard on the runtime pool failure path (line 115).

Important Files Changed

Filename Overview
framework/configstore/postgres.go Refactored into clean helper functions; two-pool lifecycle correctly implemented with explicit close of migration pool on both success and error paths.
framework/configstore/rdb.go Changed db field to atomic.Pointer[gorm.DB]; added RunMigration and RefreshConnectionPool with nil-hook guards; all s.db usages updated to s.DB().
framework/configstore/store.go Added RunMigration and RefreshConnectionPool to the ConfigStore interface with clear documentation on semantics.
framework/configstore/sqlite.go Correctly wires pass-through hooks for both migrateOnFreshFn and refreshPoolFn since SQLite has no server-side plan cache.
framework/logstore/postgres.go Two-pool pattern applied; migration close error is checked on success path but silently discarded on the runtime pool failure path (line 115).
framework/configstore/migrations.go No substantive logic changes; advisory lock mechanism is unchanged and works correctly on the throwaway migration pool.
framework/configstore/dlock_test.go Test setup updated to use atomic.Pointer and new hook fields; direct store.db accesses replaced with store.DB().
framework/configstore/rdb_test.go Test helper updated to use atomic.Pointer and stub out both function hooks with no-ops.

Reviews (5): Last reviewed commit: "run migrations on separate connection to..." | Re-trigger Greptile

@akshaydeo akshaydeo changed the base branch from 04-18-compilation_fixes to graphite-base/2831 April 18, 2026 18:49
Comment thread framework/configstore/rdb.go Outdated
Comment thread framework/configstore/postgres.go Outdated
@akshaydeo akshaydeo force-pushed the graphite-base/2831 branch from 867b4d2 to 4559d6a Compare April 18, 2026 18:50
@akshaydeo akshaydeo force-pushed the 04-19-separate_conenction_for_migrations branch from ac6d6b7 to 882c688 Compare April 18, 2026 18:50
@graphite-app graphite-app Bot changed the base branch from graphite-base/2831 to main April 18, 2026 18:50
@akshaydeo akshaydeo force-pushed the 04-19-separate_conenction_for_migrations branch from 882c688 to 55fe2be Compare April 18, 2026 18:50
Comment thread framework/configstore/postgres.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/configstore/postgres.go (1)

53-81: ⚠️ Potential issue | 🟠 Major

Defer close the migration DB connection immediately after opening.

mDb is only closed if triggerMigrations() fails. On the success path or if the second gorm.Open() call fails at line 79, the migration connection remains open, leaking a database connection.

Proposed fix
 	mDb, err := gorm.Open(postgres.New(postgres.Config{
 		DSN: dsn,
 	}), &gorm.Config{
 		Logger: newGormLogger(logger),
 	})
 	if err != nil {
 		return nil, err
 	}
+	mSqlDB, err := mDb.DB()
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		if closeErr := mSqlDB.Close(); closeErr != nil {
+			logger.Error("failed to close DB connection: %v", closeErr)
+		}
+	}()
 
 	// Run migrations
 	if err := triggerMigrations(ctx, mDb); err != nil {
-		// Closing the DB connection
-		if sqlDB, dbErr := mDb.DB(); dbErr == nil {
-			if closeErr := sqlDB.Close(); closeErr != nil {
-				logger.Error("failed to close DB connection: %v", closeErr)
-			}
-		}
 		return nil, err
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/postgres.go` around lines 53 - 81, Open migration DB
(mDb) then immediately obtain its underlying *sql.DB via mDb.DB() and defer
closing it so the connection is always released; keep using
triggerMigrations(ctx, mDb) as before and remove the current ad-hoc
close-on-error logic. Concretely, after gorm.Open that returns mDb, call sqlDB,
_ := mDb.DB() and add a defer func() { if err := sqlDB.Close(); err != nil {
logger.Error("failed to close migration DB: %v", err) } }() so the migration
connection is closed on both success and failure before you open the separate
pool (db).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/configstore/rdb.go`:
- Around line 123-126: RefreshConnectionPool on RDBConfigStore declares an error
return but has an empty body; implement it to return an error on failure similar
to Ping(). Update the method RefreshConnectionPool to use the existing DB handle
(s.db) with the provided ctx and perform a lightweight check (e.g.
s.db.WithContext(ctx).Exec("SELECT 1").Error) or obtain the underlying sql.DB
and call PingContext(ctx) (via sqlDB, e.g. sqlDB.DB().PingContext(ctx)) and
return that error so all code paths return an error value.

---

Outside diff comments:
In `@framework/configstore/postgres.go`:
- Around line 53-81: Open migration DB (mDb) then immediately obtain its
underlying *sql.DB via mDb.DB() and defer closing it so the connection is always
released; keep using triggerMigrations(ctx, mDb) as before and remove the
current ad-hoc close-on-error logic. Concretely, after gorm.Open that returns
mDb, call sqlDB, _ := mDb.DB() and add a defer func() { if err := sqlDB.Close();
err != nil { logger.Error("failed to close migration DB: %v", err) } }() so the
migration connection is closed on both success and failure before you open the
separate pool (db).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d0c8906-c647-450f-8637-05b08aa770a9

📥 Commits

Reviewing files that changed from the base of the PR and between 867b4d2 and ac6d6b7.

📒 Files selected for processing (3)
  • framework/configstore/postgres.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go

Comment thread framework/configstore/rdb.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
framework/configstore/rdb.go (1)

124-126: ⚠️ Potential issue | 🔴 Critical

RefreshConnectionPool is still non-compilable due to missing return value.

Line 124 declares an error return, but Lines 125-126 provide no return path.

💡 Minimal fix
func (s *RDBConfigStore) RefreshConnectionPool(ctx context.Context) error {
-
+	return s.db.WithContext(ctx).Exec("SELECT 1").Error
}
#!/bin/bash
set -euo pipefail

# Verify that RefreshConnectionPool declares an error return and whether its body has a return statement.
python - <<'PY'
from pathlib import Path

p = Path("framework/configstore/rdb.go")
lines = p.read_text().splitlines()

sig = "func (s *RDBConfigStore) RefreshConnectionPool(ctx context.Context) error"
start = next((i for i, l in enumerate(lines, 1) if sig in l), None)
if start is None:
    raise SystemExit("method signature not found")

brace = 0
end = None
body = []
for i in range(start, len(lines) + 1):
    line = lines[i - 1]
    brace += line.count("{")
    if i > start:
        body.append((i, line))
    brace -= line.count("}")
    if i > start and brace == 0:
        end = i
        break

has_return = any("return" in l for _, l in body)
print(f"method_range={start}-{end}")
print(f"has_return_statement={has_return}")
for n, l in body:
    print(f"{n}: {l}")
PY
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/rdb.go` around lines 124 - 126, The method
RefreshConnectionPool on RDBConfigStore currently has no return path and fails
to compile; update its implementation to perform the intended connection-pool
refresh logic and ensure it returns an error value (e.g., return nil on success
or an appropriate error on failure). Locate func (s *RDBConfigStore)
RefreshConnectionPool(ctx context.Context) error, implement the pool-refresh
steps you need (using s, ctx, and any internal pool fields/methods) and add a
final return statement returning nil for success or the encountered error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@framework/configstore/rdb.go`:
- Around line 124-126: The method RefreshConnectionPool on RDBConfigStore
currently has no return path and fails to compile; update its implementation to
perform the intended connection-pool refresh logic and ensure it returns an
error value (e.g., return nil on success or an appropriate error on failure).
Locate func (s *RDBConfigStore) RefreshConnectionPool(ctx context.Context)
error, implement the pool-refresh steps you need (using s, ctx, and any internal
pool fields/methods) and add a final return statement returning nil for success
or the encountered error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3d2cd7e2-42ca-4d9f-8651-6db84706d3da

📥 Commits

Reviewing files that changed from the base of the PR and between ac6d6b7 and 55fe2be.

📒 Files selected for processing (3)
  • framework/configstore/postgres.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • framework/configstore/store.go
  • framework/configstore/postgres.go

@akshaydeo akshaydeo force-pushed the 04-19-separate_conenction_for_migrations branch 2 times, most recently from 9fd0550 to 5dabb03 Compare April 18, 2026 19:59
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/configstore/rdb.go (1)

3844-3849: ⚠️ Potential issue | 🟠 Major

RunMigration still bypasses the fresh-connection path.

Line 3848 constructs the migrator from s.DB() instead of MigrateOnFreshConnection. That leaves the original cached-plan failure available to any caller that invokes RunMigration directly after the runtime pool has already prepared statements. This entry point should use the same throwaway-connection flow as the startup path, then refresh the runtime pool on success.

Suggested fix
 func (s *RDBConfigStore) RunMigration(ctx context.Context, migration *migrator.Migration) error {
 	if migration == nil {
 		return fmt.Errorf("migration cannot be nil")
 	}
-	m := migrator.New(s.DB(), migrator.DefaultOptions, []*migrator.Migration{migration})
-	return m.Migrate()
+	if err := s.MigrateOnFreshConnection(ctx, func(ctx context.Context, db *gorm.DB) error {
+		m := migrator.New(db.WithContext(ctx), migrator.DefaultOptions, []*migrator.Migration{migration})
+		return m.Migrate()
+	}); err != nil {
+		return err
+	}
+	return s.RefreshConnectionPool(ctx)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/rdb.go` around lines 3844 - 3849, RunMigration
currently constructs the migrator with s.DB(), bypassing the startup
throwaway-connection flow; change RunMigration to build/run the migrator via the
MigrateOnFreshConnection path used at startup (i.e. use the same
MigrateOnFreshConnection helper instead of s.DB() when creating the migrator in
RunMigration), and after a successful m.Migrate() call refresh the runtime
connection pool the same way the startup code does so prepared-statement cache
issues are cleared for future callers.
🧹 Nitpick comments (2)
framework/configstore/migrations_test.go (1)

1125-1130: Keep one test on the real pool-refresh path.

This helper now replaces refreshPoolFn with a no-op, and all other test helpers in the codebase do the same. While this keeps the existing test suites stable, the real implementation in postgres.go (which opens a fresh connection, applies pool tuning, atomically swaps, and closes the old pool) is never exercised by any unit test. Please add or preserve at least one test that exercises RefreshConnectionPool with a real PostgreSQL backend to catch regressions in the actual pool lifecycle logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/migrations_test.go` around lines 1125 - 1130, The
helper currently stubs out refreshPoolFn on RDBConfigStore making tests miss the
real pool lifecycle; restore or add one unit/integration test that invokes
RefreshConnectionPool against a real Postgres backend instead of the no-op:
update the test setup so RDBConfigStore.refreshPoolFn is not replaced for that
test (or add a new test case) and call store.RefreshConnectionPool(ctx) to
exercise the code path implemented in postgres.go (the actual pool-open, tuning,
swap and close sequence), using the same DB instance created by
store.db.Store(db) so the real pool swap runs; ensure teardown closes pools to
avoid leaks.
framework/configstore/rdb_test.go (1)

56-62: Add a hook-level regression test for the new migration lifecycle.

This helper now wires migrateOnFreshFn/refreshPoolFn, but the file still doesn't assert that migration code actually goes through those hooks. A small spy-based test here would catch regressions where DDL accidentally falls back to DB() again.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/rdb_test.go` around lines 56 - 62, Add a spy-based test
that ensures migrations go through the hook fields instead of directly calling
s.DB(): create an RDBConfigStore instance and replace s.migrateOnFreshFn with a
spy function that records it was invoked and captures the *gorm.DB passed in,
and replace s.refreshPoolFn with a spy that records invocation; also replace
s.db.Store(db) with a sentinel db (or wrap the DB accessor) that will set a flag
or panic if s.DB() is invoked directly during the migration path; then run the
same migration-exercising call used in the test suite (the public migration path
exercised by this file) and assert the migrateOnFreshFn and refreshPoolFn spies
were called and that the direct s.DB() access flag/panic was not triggered.
Ensure you reference RDBConfigStore, migrateOnFreshFn, refreshPoolFn and DB() in
the test so regressions are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/logstore/postgres.go`:
- Around line 64-73: The migration "closePool" currently swallows sqlDB.Close()
errors and allows NewLogStore to continue initializing; change the close logic
so that closePool (used for the throwaway migration pool) returns an error when
sqlDB.Close() fails and propagate that error up in NewLogStore to abort startup
instead of proceeding to open the runtime pool. Concretely, update the closePool
function referenced as closePool/db.DB()/sqlDB.Close to return an error, check
that error after calling closePool in NewLogStore, and return the error from
NewLogStore (failing startup) if closing the migration pool fails; apply the
same change to the other close call at the second location that currently logs
and continues (lines referenced in review: 98-101).

---

Outside diff comments:
In `@framework/configstore/rdb.go`:
- Around line 3844-3849: RunMigration currently constructs the migrator with
s.DB(), bypassing the startup throwaway-connection flow; change RunMigration to
build/run the migrator via the MigrateOnFreshConnection path used at startup
(i.e. use the same MigrateOnFreshConnection helper instead of s.DB() when
creating the migrator in RunMigration), and after a successful m.Migrate() call
refresh the runtime connection pool the same way the startup code does so
prepared-statement cache issues are cleared for future callers.

---

Nitpick comments:
In `@framework/configstore/migrations_test.go`:
- Around line 1125-1130: The helper currently stubs out refreshPoolFn on
RDBConfigStore making tests miss the real pool lifecycle; restore or add one
unit/integration test that invokes RefreshConnectionPool against a real Postgres
backend instead of the no-op: update the test setup so
RDBConfigStore.refreshPoolFn is not replaced for that test (or add a new test
case) and call store.RefreshConnectionPool(ctx) to exercise the code path
implemented in postgres.go (the actual pool-open, tuning, swap and close
sequence), using the same DB instance created by store.db.Store(db) so the real
pool swap runs; ensure teardown closes pools to avoid leaks.

In `@framework/configstore/rdb_test.go`:
- Around line 56-62: Add a spy-based test that ensures migrations go through the
hook fields instead of directly calling s.DB(): create an RDBConfigStore
instance and replace s.migrateOnFreshFn with a spy function that records it was
invoked and captures the *gorm.DB passed in, and replace s.refreshPoolFn with a
spy that records invocation; also replace s.db.Store(db) with a sentinel db (or
wrap the DB accessor) that will set a flag or panic if s.DB() is invoked
directly during the migration path; then run the same migration-exercising call
used in the test suite (the public migration path exercised by this file) and
assert the migrateOnFreshFn and refreshPoolFn spies were called and that the
direct s.DB() access flag/panic was not triggered. Ensure you reference
RDBConfigStore, migrateOnFreshFn, refreshPoolFn and DB() in the test so
regressions are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8f1ac7f0-7777-4dab-b0c3-14a1ffb6148c

📥 Commits

Reviewing files that changed from the base of the PR and between 55fe2be and 9fd0550.

📒 Files selected for processing (12)
  • framework/configstore/dlock_test.go
  • framework/configstore/encryption.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_test.go
  • framework/configstore/postgres.go
  • framework/configstore/prompts.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/sqlite.go
  • framework/configstore/store.go
  • framework/logstore/postgres.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • framework/configstore/store.go
  • framework/configstore/postgres.go

Comment thread framework/logstore/postgres.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
framework/logstore/postgres.go (1)

64-73: ⚠️ Potential issue | 🟠 Major

Fail initialization if the migration pool cannot be closed.

Line 64-73 swallows close failures, and Line 98 continues startup even if migration-pool close fails. That weakens the two-pool guarantee and can leave pre-migration connections alive. Please make closePool return error and fail fast when closing mDb fails.

💡 Proposed fix
-	closePool := func(db *gorm.DB) {
+	closePool := func(db *gorm.DB) error {
 		if db == nil {
-			return
+			return nil
 		}
-		if sqlDB, err := db.DB(); err == nil {
-			if closeErr := sqlDB.Close(); closeErr != nil {
-				logger.Error("failed to close DB connection: %v", closeErr)
-			}
+		sqlDB, err := db.DB()
+		if err != nil {
+			return err
 		}
+		return sqlDB.Close()
 	}
@@
-	if err := mDb.Raw("SELECT current_setting('server_version_num')::int").Scan(&pgVersionNum).Error; err != nil {
-		closePool(mDb)
+	if err := mDb.Raw("SELECT current_setting('server_version_num')::int").Scan(&pgVersionNum).Error; err != nil {
+		_ = closePool(mDb)
 		return nil, err
 	}
 	if pgVersionNum < 160000 {
-		closePool(mDb)
+		_ = closePool(mDb)
 		return nil, fmt.Errorf("postgres version is lower than 16, please upgrade to 16 or higher")
 	}
@@
-	if err := triggerMigrations(ctx, mDb); err != nil {
-		closePool(mDb)
+	if err := triggerMigrations(ctx, mDb); err != nil {
+		_ = closePool(mDb)
 		return nil, err
 	}
-	closePool(mDb)
+	if err := closePool(mDb); err != nil {
+		return nil, fmt.Errorf("close migration db connection: %w", err)
+	}
@@
 	sqlDB, err := db.DB()
 	if err != nil {
-		closePool(db)
+		_ = closePool(db)
 		return nil, err
 	}

Also applies to: 86-98

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/logstore/postgres.go` around lines 64 - 73, The closePool helper
currently swallows errors; change closePool to return error (signature
closePool(db *gorm.DB) error), propagate and check its return where it's used
(notably when closing the migration pool mDb), and if closePool returns an error
log it and return that error to abort initialization (fail fast) so startup does
not continue when closing mDb fails; ensure the implementation returns any
sqlDB.Close() error (use logger.Errorf or logger.Error with the error) instead
of ignoring it, and update all callers (the places around mDb handling and the
other pool close at lines shown) to handle the error and stop startup on
failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/configstore/rdb.go`:
- Around line 178-185: The code is incorrectly caching the runtime DB handle
(e.g., txDB := s.DB() or baseQuery := s.DB()) which can be closed by
RefreshConnectionPool; update all non-transactional sites in this file
(including the blocks around the existing DB() use and the other occurrences
around the 362-368 region) to call s.DB() immediately before each database
operation instead of storing it once up-front, ensure transactional code still
uses a single *gorm.DB from Begin/Commit/Rollback when needed, and remove or
refactor any txDB/baseQuery variables in non-transactional methods so every
statement invokes s.DB() anew to avoid using a closed *sql.DB.
- Around line 195-204: The methods RDBConfigStore.RunMigration and
RefreshConnectionPool currently call the function fields migrateOnFreshFn and
refreshPoolFn without nil checks; change each to guard the corresponding field
on s (migrateOnFreshFn and refreshPoolFn) and return a clear error if the hook
is nil rather than dereferencing and panicking—e.g., check s.migrateOnFreshFn !=
nil in RunMigration and s.refreshPoolFn != nil in RefreshConnectionPool and
return an error (with context mentioning the method name) when the hook is not
set; otherwise call the hook as before.

In `@framework/logstore/asyncjob_test.go`:
- Around line 89-92: capturedCtx is nil when you call SetValue, causing a panic;
before any SetValue calls (the instances using capturedCtx, SetValue,
schemas.BifrostContext and
schemas.BifrostContextKey*/BifrostContextKeyVirtualKey), initialize capturedCtx
(e.g. assign it a concrete value via a constructor like
schemas.NewBifrostContext() if available or &schemas.BifrostContext{}), then
call SetValue; apply this initialization at each occurrence where capturedCtx is
declared and immediately used.

---

Duplicate comments:
In `@framework/logstore/postgres.go`:
- Around line 64-73: The closePool helper currently swallows errors; change
closePool to return error (signature closePool(db *gorm.DB) error), propagate
and check its return where it's used (notably when closing the migration pool
mDb), and if closePool returns an error log it and return that error to abort
initialization (fail fast) so startup does not continue when closing mDb fails;
ensure the implementation returns any sqlDB.Close() error (use logger.Errorf or
logger.Error with the error) instead of ignoring it, and update all callers (the
places around mDb handling and the other pool close at lines shown) to handle
the error and stop startup on failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 703a7267-e1f6-48d1-8357-45de6a08809d

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd0550 and 5dabb03.

📒 Files selected for processing (13)
  • framework/configstore/dlock_test.go
  • framework/configstore/encryption.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_test.go
  • framework/configstore/postgres.go
  • framework/configstore/prompts.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/sqlite.go
  • framework/configstore/store.go
  • framework/logstore/asyncjob_test.go
  • framework/logstore/postgres.go
✅ Files skipped from review due to trivial changes (4)
  • framework/configstore/rdb_test.go
  • framework/configstore/prompts.go
  • framework/configstore/postgres.go
  • framework/configstore/encryption_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • framework/configstore/sqlite.go
  • framework/configstore/encryption.go
  • framework/configstore/dlock_test.go
  • framework/configstore/store.go

Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/rdb.go
Comment thread framework/logstore/asyncjob_test.go Outdated
@akshaydeo akshaydeo force-pushed the 04-19-separate_conenction_for_migrations branch from 5dabb03 to 98ebb2f Compare April 19, 2026 02:36
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/configstore/rdb.go (1)

3198-3224: ⚠️ Potential issue | 🟠 Major

Reject empty ScopeID for non-global routing rules.

These checks only reject nil. A rule with a non-global scope and ScopeID: bifrost.Ptr("") still gets created or updated here, but GetRoutingRulesByScope later rejects empty scope IDs, so the rule becomes unreachable.

🛡️ Suggested guard
-	if rule.Scope != "" && rule.Scope != "global" && rule.ScopeID == nil {
+	if rule.Scope != "" && rule.Scope != "global" && (rule.ScopeID == nil || strings.TrimSpace(*rule.ScopeID) == "") {
 		return fmt.Errorf("scopeID is required for non-global scope '%s'", rule.Scope)
 	}
@@
-	if rule.Scope != "" && rule.Scope != "global" && rule.ScopeID == nil {
+	if rule.Scope != "" && rule.Scope != "global" && (rule.ScopeID == nil || strings.TrimSpace(*rule.ScopeID) == "") {
 		return fmt.Errorf("scopeID is required for non-global scope '%s'", rule.Scope)
 	}
Based on learnings: Enforce routing rules: scopeID must be set for all non-global scopes (team, customer, virtual_key). Only global scope is allowed to have an empty or NULL scopeID.

Also applies to: 3247-3273

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/rdb.go` around lines 3198 - 3224, The validation
currently only rejects nil ScopeID for non-global scopes; update the checks in
the save/update blocks that use rule.Scope and rule.ScopeID (the block using
database := s.DB(), query := database.WithContext(ctx)... and the similar block
around lines 3247-3273) to also reject empty-string ScopeIDs (e.g., if
rule.Scope != "" && rule.Scope != "global" && (rule.ScopeID == nil ||
*rule.ScopeID == "") return an error). Ensure the error message mirrors the
existing one and reference GetRoutingRulesByScope behavior by enforcing that
non-global scopes (team, customer, virtual_key) must have a non-empty ScopeID.
♻️ Duplicate comments (1)
framework/configstore/rdb.go (1)

377-408: ⚠️ Potential issue | 🟠 Major

Stop caching s.DB() across multi-step operations.

Line 377, Line 1211, and Line 3198 still pin the pre-refresh handle in txDB, baseQuery, or database and then reuse it for later statements. After RefreshConnectionPool swaps and closes the old pool, those later calls can fail with a closed-DB error. Please apply the same per-operation s.DB() pattern here and at the other remaining non-transactional sites in this file.

♻️ Example fix pattern
-	var txDB *gorm.DB
-	if len(tx) > 0 {
-		txDB = tx[0]
-	} else {
-		txDB = s.DB()
-	}
+	currentDB := func() *gorm.DB {
+		if len(tx) > 0 && tx[0] != nil {
+			return tx[0]
+		}
+		return s.DB()
+	}
@@
-		if err := txDB.WithContext(ctx).Clauses(
+		if err := currentDB().WithContext(ctx).Clauses(
 			clause.OnConflict{

Also applies to: 1211-1241, 3198-3303

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/rdb.go` around lines 377 - 408, UpdateProvidersConfig
(and other non-transactional code in this file like usages that built a
persistent baseQuery or stored a database handle) currently caches a single DB
handle in variables such as txDB/database/baseQuery and reuses it across
multiple statements, which breaks when RefreshConnectionPool swaps and closes
the old pool; fix by calling s.DB() immediately before each independent DB
operation instead of reusing a previously stored handle—e.g., in
UpdateProvidersConfig call cur := s.DB() (or use s.DB().WithContext(ctx) inline)
for the Create/Clauses call inside the loop (and apply the same change to other
sites that assign txDB/baseQuery/database outside the per-operation scope) so
every statement uses a fresh live connection handle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@framework/configstore/rdb.go`:
- Around line 3198-3224: The validation currently only rejects nil ScopeID for
non-global scopes; update the checks in the save/update blocks that use
rule.Scope and rule.ScopeID (the block using database := s.DB(), query :=
database.WithContext(ctx)... and the similar block around lines 3247-3273) to
also reject empty-string ScopeIDs (e.g., if rule.Scope != "" && rule.Scope !=
"global" && (rule.ScopeID == nil || *rule.ScopeID == "") return an error).
Ensure the error message mirrors the existing one and reference
GetRoutingRulesByScope behavior by enforcing that non-global scopes (team,
customer, virtual_key) must have a non-empty ScopeID.

---

Duplicate comments:
In `@framework/configstore/rdb.go`:
- Around line 377-408: UpdateProvidersConfig (and other non-transactional code
in this file like usages that built a persistent baseQuery or stored a database
handle) currently caches a single DB handle in variables such as
txDB/database/baseQuery and reuses it across multiple statements, which breaks
when RefreshConnectionPool swaps and closes the old pool; fix by calling s.DB()
immediately before each independent DB operation instead of reusing a previously
stored handle—e.g., in UpdateProvidersConfig call cur := s.DB() (or use
s.DB().WithContext(ctx) inline) for the Create/Clauses call inside the loop (and
apply the same change to other sites that assign txDB/baseQuery/database outside
the per-operation scope) so every statement uses a fresh live connection handle.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a9290406-9fe1-4a43-932b-679e10208bc5

📥 Commits

Reviewing files that changed from the base of the PR and between 5dabb03 and 98ebb2f.

📒 Files selected for processing (13)
  • framework/configstore/dlock_test.go
  • framework/configstore/encryption.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_test.go
  • framework/configstore/postgres.go
  • framework/configstore/prompts.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/sqlite.go
  • framework/configstore/store.go
  • framework/logstore/asyncjob_test.go
  • framework/logstore/postgres.go
✅ Files skipped from review due to trivial changes (5)
  • framework/configstore/encryption_test.go
  • framework/logstore/asyncjob_test.go
  • framework/configstore/prompts.go
  • framework/configstore/encryption.go
  • framework/configstore/postgres.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • framework/configstore/sqlite.go
  • framework/logstore/postgres.go
  • framework/configstore/migrations.go

@akshaydeo akshaydeo force-pushed the 04-19-separate_conenction_for_migrations branch from 98ebb2f to f0e0659 Compare April 19, 2026 19:45
@akshaydeo akshaydeo mentioned this pull request Apr 19, 2026
18 tasks
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
framework/configstore/postgres.go (1)

40-50: ⚠️ Potential issue | 🟠 Major

Propagate pool-close failures instead of only logging them.

closeDbConn currently swallows db.DB() / sqlDB.Close() errors, so Line 115, Line 137, and Line 156 can all report success while the old pool is still alive. That weakens the main guarantee of this PR: the pre-migration/runtime pool may survive after DDL, and RefreshConnectionPool can silently fail to complete.

💡 Proposed fix
-func closeDbConn(db *gorm.DB, logger schemas.Logger) {
-	sqlDB, err := db.DB()
-	if err != nil {
-		logger.Error("failed to resolve *sql.DB for close: %v", err)
-		return
-	}
-	if err := sqlDB.Close(); err != nil {
-		logger.Error("failed to close DB connection: %v", err)
-	}
+func closeDbConn(db *gorm.DB) error {
+	if db == nil {
+		return nil
+	}
+	sqlDB, err := db.DB()
+	if err != nil {
+		return fmt.Errorf("resolve *sql.DB for close: %w", err)
+	}
+	if err := sqlDB.Close(); err != nil {
+		return fmt.Errorf("close db connection: %w", err)
+	}
+	return nil
 }

Then propagate the returned error at the migration-pool close, the deferred migrateOnFreshFn close, and the old-pool close in refreshPoolFn (joining it with the primary error on failure paths if needed).

Run the following read-only script to confirm that closeDbConn cannot currently propagate close failures and that all callers ignore them:

#!/bin/bash
set -euo pipefail

echo "closeDbConn definition:"
rg -n -C2 'func closeDbConn\(db \*gorm\.DB, logger schemas\.Logger\)' framework/configstore/postgres.go

echo
echo "closeDbConn call sites:"
rg -n -C2 'closeDbConn\(' framework/configstore/postgres.go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/configstore/postgres.go` around lines 40 - 50, closeDbConn
currently swallows errors; change its signature from closeDbConn(db *gorm.DB,
logger schemas.Logger) to return error (e.g., closeDbConn(...) error), have it
return the db.DB() or sqlDB.Close() error (wrapped with context via fmt.Errorf
or errors.Join when needed), then update all call sites (RefreshConnectionPool,
refreshPoolFn, migrateOnFreshFn and any deferred closes) to check and
propagate/aggregate the returned error instead of silently logging—on failure
paths join the pool-close error with the primary error (errors.Join or similar)
and on success return nil so callers can correctly report when the old pool
failed to close.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/configstore/migrations.go`:
- Around line 80-89: Update the exported RunSingleMigration function's docstring
to explicitly state that callers supplying their own *gorm.DB are responsible
for serialization/locking: e.g., "If you call RunSingleMigration directly
(outside MigrateOnFreshConnection callbacks), you must acquire the appropriate
advisory/serialization lock for multi-node safety; the built-in
triggerMigrations path already acquires this lock." Alternatively, if you prefer
API-level enforcement, add an optional parameter (e.g., acquireLock bool or a
LockProvider interface) to RunSingleMigration and implement advisory lock
acquisition when true; reference the RunSingleMigration function and the
triggerMigrations code path when making this change.

---

Duplicate comments:
In `@framework/configstore/postgres.go`:
- Around line 40-50: closeDbConn currently swallows errors; change its signature
from closeDbConn(db *gorm.DB, logger schemas.Logger) to return error (e.g.,
closeDbConn(...) error), have it return the db.DB() or sqlDB.Close() error
(wrapped with context via fmt.Errorf or errors.Join when needed), then update
all call sites (RefreshConnectionPool, refreshPoolFn, migrateOnFreshFn and any
deferred closes) to check and propagate/aggregate the returned error instead of
silently logging—on failure paths join the pool-close error with the primary
error (errors.Join or similar) and on success return nil so callers can
correctly report when the old pool failed to close.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad683a0d-40a4-4321-acd3-040ea79a3f17

📥 Commits

Reviewing files that changed from the base of the PR and between 98ebb2f and f0e0659.

📒 Files selected for processing (13)
  • framework/configstore/dlock_test.go
  • framework/configstore/encryption.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_test.go
  • framework/configstore/postgres.go
  • framework/configstore/prompts.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/sqlite.go
  • framework/configstore/store.go
  • framework/logstore/asyncjob_test.go
  • framework/logstore/postgres.go
✅ Files skipped from review due to trivial changes (2)
  • framework/configstore/prompts.go
  • framework/configstore/rdb.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • framework/configstore/sqlite.go
  • framework/configstore/store.go
  • framework/logstore/asyncjob_test.go
  • framework/configstore/encryption_test.go

Comment thread framework/configstore/migrations.go
Copy link
Copy Markdown
Contributor Author

akshaydeo commented Apr 20, 2026

Merge activity

  • Apr 20, 6:39 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 20, 6:40 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo akshaydeo merged commit 1ce554e into main Apr 20, 2026
11 of 16 checks passed
@akshaydeo akshaydeo deleted the 04-19-separate_conenction_for_migrations branch April 20, 2026 06:40
dominictayloruk pushed a commit to dominictayloruk/bifrost that referenced this pull request Apr 21, 2026
## Summary

Fixes a Postgres error (`ERROR: cached plan must not change result type (SQLSTATE 0A000)`) that occurred when running database migrations on the same connection pool used for regular queries. Migrations are now run on a dedicated, short-lived database connection that is closed after migrations complete, while the main connection pool is created separately afterward.

## Changes

- Migrations are now executed on a separate, dedicated database connection rather than the main connection pool. This prevents prepared statement cache conflicts that arise when schema changes (from migrations) invalidate cached query plans on an existing connection.
- The migration connection is explicitly closed after migrations finish, regardless of success or failure.
- A `RefreshConnectionPool` method has been added to the `ConfigStore` interface and `RDBConfigStore` to support refreshing the connection pool when needed.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Run the service against a Postgres instance where migrations need to be applied and verify that the `cached plan must not change result type` error no longer occurs on startup.

```sh
go test ./framework/configstore/...
```

## Breaking changes

- [x] Yes
- [ ] No

`ConfigStore` interface now includes `RefreshConnectionPool(ctx context.Context) error`. Any external implementations of this interface will need to implement this new method.

## Related issues

Closes the issue related to `ERROR: cached plan must not change result type (SQLSTATE 0A000)` during migrations.

## Security considerations

No security implications. The migration connection uses the same credentials and SSL configuration as the main pool and is closed immediately after use.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
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.

2 participants