Skip to content

onboarding widget backend changes - #3605

Merged
akshaydeo merged 1 commit into
devfrom
05-20-onboarding_widget_backend_changes
May 20, 2026
Merged

onboarding widget backend changes#3605
akshaydeo merged 1 commit into
devfrom
05-20-onboarding_widget_backend_changes

Conversation

@akshaydeo

@akshaydeo akshaydeo commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a metadata_json column to config_client that stores a free-form JSON blob of UI/admin preferences (e.g. onboarding_dismissed). This blob is intentionally excluded from the ClientConfig API struct so that config.json sync can never overwrite it. A new POST /api/config/metadata endpoint allows callers to merge patches into the blob, and GET /api/config now includes the metadata in its response.

Changes

  • Added migrationAddClientConfigMetadataColumn to create the metadata_json column on config_client with a rollback path.
  • Added MetadataJSON (persisted) and Metadata (virtual) fields to TableClientConfig, with BeforeSave/AfterFind hooks to marshal/unmarshal the JSON blob.
  • UpdateClientConfig now reads and preserves MetadataJSON inside its DELETE+CREATE transaction so config writes never clobber UI preferences.
  • Added GetClientMetadata and UpdateClientMetadata to RDBConfigStore. UpdateClientMetadata performs a targeted UPDATE on metadata_json only, merging the patch and removing keys whose value is nil.
  • Exposed both methods on the ConfigStore interface.
  • Registered POST /api/config/metadata in the HTTP transport; GET /api/config now appends the metadata blob to the response when non-empty.
  • Added MetadataKeyOnboardingDismissed constant as the first typed key in the metadata blob.
  • Added metadata field to the BifrostConfig TypeScript type.
  • Added stub implementations of GetClientMetadata and UpdateClientMetadata to MockConfigStore in tests.

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

# Core/Transports
go test ./framework/configstore/... ./transports/bifrost-http/...

# Verify migration runs cleanly on a fresh or existing DB by starting the server
# and confirming the metadata_json column exists on config_client.

# Set a metadata key
curl -X POST http://localhost:8080/api/config/metadata \
  -H "Content-Type: application/json" \
  -d '{"onboarding_dismissed": true}'
# Expected: {"success": true}

# Read it back
curl http://localhost:8080/api/config
# Expected: response includes "metadata": {"onboarding_dismissed": true}

# Clear a key by passing null
curl -X POST http://localhost:8080/api/config/metadata \
  -H "Content-Type: application/json" \
  -d '{"onboarding_dismissed": null}'
# Expected: {"success": true}, metadata key removed

# UI
cd ui
pnpm i
pnpm build

Breaking changes

  • Yes
  • No

Related issues

Security considerations

The POST /api/config/metadata endpoint is gated by the same middleware chain as the rest of /api/config, so it inherits existing auth controls. The metadata blob is a free-form store; callers should avoid writing secrets or PII into it, as it is returned in plaintext via GET /api/config.

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

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d04611d-fc8d-4e2e-b07d-2130bb9f2a89

📥 Commits

Reviewing files that changed from the base of the PR and between 3da31f2 and 135a863.

📒 Files selected for processing (10)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/clientconfig_test.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config_test.go
  • ui/lib/types/config.ts

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Persistent client configuration metadata for UI/admin preferences.
    • Config responses now optionally include metadata for richer client-side behavior.
    • New API endpoint to apply partial metadata updates, including key removal.
    • Added an exported metadata key for onboarding dismissal and a typed metadata field in the client config surface.
  • Bug Fixes
    • Client metadata is preserved when updating other client config fields.
  • Tests
    • Added coverage for metadata updates, removals, preservation, merging, and error handling when no config exists.

Walkthrough

Adds persistent client metadata: a metadata_json column and model hooks, ConfigStore Get/Update metadata APIs (transactional merge with nil deletes), UpdateClientConfig preservation of metadata, an HTTP metadata patch endpoint, frontend type, tests, and a migration.

Changes

Client configuration metadata support

Layer / File(s) Summary
Database schema and migration
framework/configstore/tables/clientconfig.go, framework/configstore/tables/config.go, framework/configstore/migrations.go, tests
Adds persisted MetadataJSON and virtual Metadata with BeforeSave/AfterFind hooks; exported MetadataKeyOnboardingDismissed constant; migration to add/drop metadata_json; unit tests for AfterFind.
Config store interface and RDB implementation
framework/configstore/store.go, framework/configstore/rdb.go, framework/configstore/rdb_test.go
Adds GetClientMetadata and UpdateClientMetadata. RDB preserves metadata_json during UpdateClientConfig, returns non-nil maps from GetClientMetadata, and applies transactional merge patches (nil deletes) with deterministic JSON marshalling. Tests cover merge, deletion, not-found, and preservation across updates.
HTTP API endpoints and handlers
transports/bifrost-http/handlers/config.go, transports/bifrost-http/lib/config_test.go
Registers POST /api/config/metadata routed to updateMetadata; getConfig includes optional metadata when present. Handler validates patch, persists via UpdateClientMetadata, and maps missing client config to HTTP 409. MockConfigStore stubs added for handler tests.
Frontend configuration types
ui/lib/types/config.ts
BifrostConfig adds optional metadata?: Record<string, unknown> for client consumption.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HTTPHandler
  participant RDBConfigStore
  participant Database
  Client->>HTTPHandler: POST /api/config/metadata {patch}
  activate HTTPHandler
  HTTPHandler->>RDBConfigStore: UpdateClientMetadata(patch)
  activate RDBConfigStore
  RDBConfigStore->>Database: SELECT metadata_json FOR UPDATE
  Database-->>RDBConfigStore: existing metadata_json
  RDBConfigStore->>RDBConfigStore: Merge patch (nil deletes), MarshalSorted
  RDBConfigStore->>Database: UPDATE config_client SET metadata_json=...
  Database-->>RDBConfigStore: update result
  RDBConfigStore->>Database: COMMIT
  deactivate RDBConfigStore
  HTTPHandler-->>Client: { "success": true } or error
  deactivate HTTPHandler
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • danpiths
  • roroghost17

Poem

🐰 I hopped through rows of JSON bright,
Marshaled maps and kept them tight,
Handlers whisper patches through,
DB remembers prefs like new,
Tests nod: metadata sleeps alright.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% 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 'onboarding widget backend changes' is vague and overly broad, using non-descriptive language that doesn't clearly convey the specific nature of the changes (metadata persistence, API endpoints, database migration). Revise the title to be more specific, such as 'Add client metadata storage and API endpoints' or 'Add metadata_json column and config metadata endpoints' to better reflect the main technical changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering summary, changes, type, affected areas, testing instructions, breaking changes, security considerations, and a detailed checklist with most items completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 05-20-onboarding_widget_backend_changes

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.12.2)

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.

akshaydeo commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@akshaydeo
akshaydeo marked this pull request as ready for review May 19, 2026 21:33
@greptile-apps

greptile-apps Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge; the core read-modify-write path is correctly protected with FOR UPDATE and the metadata column is cleanly isolated from the config.json sync path.

The locking strategy (dbForUpdate), transaction isolation, and the metadata-preservation logic in UpdateClientConfig are all correct. The two findings are a dead-code ErrNotFound guard in the GET handler and a slightly unconventional HTTP status code choice — neither affects data correctness or runtime safety.

transports/bifrost-http/handlers/config.go — dead-code ErrNotFound guard and HTTP status code choice are worth a quick look before merge.

Important Files Changed

Filename Overview
framework/configstore/migrations.go Adds migrationAddClientConfigMetadataColumn — idempotent column addition with rollback path, follows existing migration patterns correctly.
framework/configstore/rdb.go Adds GetClientMetadata / UpdateClientMetadata and metadata-preservation logic in UpdateClientConfig; FOR UPDATE locking correctly applied via dbForUpdate.
framework/configstore/tables/clientconfig.go Adds MetadataJSON/Metadata fields with BeforeSave/AfterFind hooks; BeforeSave correctly skips metadata serialization when Metadata is nil, preserving any MetadataJSON value set by callers.
framework/configstore/rdb_test.go Adds integration tests for merge semantics, nested patch, nil-deletion, no-row guard, and metadata preservation across UpdateClientConfig — good coverage of all main paths.
transports/bifrost-http/handlers/config.go Adds POST /api/config/metadata handler and plumbs metadata into GET /api/config; contains a dead-code ErrNotFound guard and an unconventional HTTP 409 status for uninitialized config.
framework/configstore/store.go Extends ConfigStore interface with GetClientMetadata and UpdateClientMetadata.
framework/configstore/tables/clientconfig_test.go New unit tests for AfterFind covering both the populate and clear code paths.
transports/bifrost-http/lib/config_test.go Adds stub GetClientMetadata/UpdateClientMetadata to MockConfigStore to satisfy the updated interface.
framework/configstore/tables/config.go Adds MetadataKeyOnboardingDismissed typed constant for the metadata blob key.
ui/lib/types/config.ts Adds optional metadata?: Record<string, unknown> to BifrostConfig TypeScript type.

Reviews (5): Last reviewed commit: "onboarding widget backend changes" | Re-trigger Greptile

Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/rdb.go Outdated
Comment thread framework/configstore/rdb.go Outdated
Comment thread transports/bifrost-http/lib/config_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/configstore/rdb.go`:
- Around line 551-552: The metadata merge is doing a read-modify-write without a
lock in UpdateClientMetadata (and similarly in UpdateClientConfig), so
concurrent calls can overwrite each other or silently be dropped when UPDATE
affects 0 rows; change the read to acquire a row lock (SELECT ... FOR UPDATE)
when loading the singleton row (use GORM row locking via
tx.Clauses(clause.Locking{Strength:"UPDATE"}).First(&existing) or equivalent) to
serialize merges, and after performing the UPDATE check RowsAffected and treat a
zero-row update as a retry/failure path (retry the read-with-lock and merge or
return a clear error) instead of silently proceeding. Ensure the fix is applied
in the code paths using tx.First(&existing) at the shown locations
(UpdateClientMetadata and the similar block at the other location).
- Around line 268-270: The current read of metadata_json into existing via
tx.Select("metadata_json").First(&existing) is not locked and can be stale;
change this to acquire the same row-level lock used elsewhere before copying
metadata (i.e., perform the SELECT with a FOR UPDATE / row-lock clause on
tables.TableClientConfig via the transaction tx when calling First on existing)
so the DELETE+CREATE in this transaction serializes with concurrent
UpdateClientMetadata writes and prevents overwriting newer metadata.
- Around line 570-571: The current code path that does
tx.Create(&tables.TableClientConfig{MetadataJSON: string(data)}) creates a
skeletal config_client row with only metadata_json, which breaks GetMCPConfig's
"no-row => use defaults" behavior; instead, before inserting check that the
primary client config row exists (the same row used by your client-config
initialization path) and only allow metadata-only writes if that main client
config is present, or populate the new tables.TableClientConfig with the same
default fields used by the normal client-config initialization routine so the
inserted row has full default values; update the code around tx.Create(...) to
either validate existence of the main client config or seed all default fields
(and reference GetMCPConfig) so downstream logic continues to use intended
defaults.
🪄 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: d17d0b91-8067-407b-a90a-9b5032fe5907

📥 Commits

Reviewing files that changed from the base of the PR and between 9538a80 and 99e8bd0.

📒 Files selected for processing (8)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config_test.go
  • ui/lib/types/config.ts

Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/rdb.go Outdated
Comment thread framework/configstore/rdb.go Outdated
@akshaydeo
akshaydeo force-pushed the 05-20-onboarding_widget_backend_changes branch from 99e8bd0 to a027d50 Compare May 20, 2026 04:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/configstore/rdb.go`:
- Around line 551-554: The check in the dbForUpdate(...).First(&existing) error
path returns a plain fmt.Errorf which hides the standard "not found" sentinel;
change that return to wrap or return the package's ErrNotFound sentinel (e.g.
return fmt.Errorf("%w: client config must be initialized before metadata can be
updated", ErrNotFound) or simply return ErrNotFound with context) so callers of
the ConfigStore can detect the missing-config case; update the branch that
currently references existing/dbForUpdate to return ErrNotFound instead of a
plain error string.

In `@framework/configstore/tables/clientconfig.go`:
- Around line 203-207: When rehydrating cc.Metadata from cc.MetadataJSON in
TableClientConfig, reset cc.Metadata to an empty map or nil before calling
json.Unmarshal to avoid merging into a previously populated map and retaining
stale keys; update the block that checks cc.MetadataJSON (the code referencing
cc.MetadataJSON and cc.Metadata) to clear or reinitialize cc.Metadata right
before unmarshalling so the resulting map reflects only the JSON payload.
🪄 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: 4b0ce748-16b0-4633-b98f-451aa8bea290

📥 Commits

Reviewing files that changed from the base of the PR and between 99e8bd0 and a027d50.

📒 Files selected for processing (9)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config_test.go
  • ui/lib/types/config.ts

Comment thread framework/configstore/rdb.go
Comment thread framework/configstore/tables/clientconfig.go
@akshaydeo
akshaydeo force-pushed the 05-20-onboarding_widget_backend_changes branch from a027d50 to 51e6ecd Compare May 20, 2026 05:33
@coderabbitai
coderabbitai Bot requested review from danpiths and roroghost17 May 20, 2026 05:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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/tables/clientconfig.go (1)

203-209: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear Metadata on empty MetadataJSON.

This hook still leaves a previously populated cc.Metadata intact when the same TableClientConfig instance is reused for a row whose metadata_json is empty, so later reads can surface stale metadata.

Suggested fix
-	if cc.MetadataJSON != "" {
+	if cc.MetadataJSON != "" {
 		var metadata map[string]any
 		if err := json.Unmarshal([]byte(cc.MetadataJSON), &metadata); err != nil {
 			return err
 		}
 		cc.Metadata = metadata
+	} else {
+		cc.Metadata = nil
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/configstore/tables/clientconfig.go` around lines 203 - 209, The
current logic only unmarshals into cc.Metadata when cc.MetadataJSON != "" but
does not clear cc.Metadata when MetadataJSON is empty, leaving stale data;
update the same hook (the block referencing cc.MetadataJSON and cc.Metadata) to
explicitly set cc.Metadata = nil (or an empty map) when cc.MetadataJSON == "" so
previous metadata is cleared, and keep the existing json.Unmarshal path
unchanged to populate cc.Metadata when JSON is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@transports/bifrost-http/handlers/config.go`:
- Around line 194-198: The handler logs a warning for any error returned by
h.store.ConfigStore.GetClientMetadata (causing noisy logs when
configstore.ErrNotFound is returned on fresh installs); update the error
handling in the GetClientMetadata block to treat configstore.ErrNotFound as
expected (do not call logger.Warn for that case—either skip logging or use a
debug/trace log) and only call logger.Warn("failed to get client metadata...")
for other errors returned by GetClientMetadata.

---

Duplicate comments:
In `@framework/configstore/tables/clientconfig.go`:
- Around line 203-209: The current logic only unmarshals into cc.Metadata when
cc.MetadataJSON != "" but does not clear cc.Metadata when MetadataJSON is empty,
leaving stale data; update the same hook (the block referencing cc.MetadataJSON
and cc.Metadata) to explicitly set cc.Metadata = nil (or an empty map) when
cc.MetadataJSON == "" so previous metadata is cleared, and keep the existing
json.Unmarshal path unchanged to populate cc.Metadata when JSON is present.
🪄 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: dfadbafc-2aab-448b-b5e8-88d827fba548

📥 Commits

Reviewing files that changed from the base of the PR and between a027d50 and 51e6ecd.

📒 Files selected for processing (10)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/clientconfig_test.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config_test.go
  • ui/lib/types/config.ts
✅ Files skipped from review due to trivial changes (2)
  • framework/configstore/tables/clientconfig_test.go
  • ui/lib/types/config.ts

Comment thread transports/bifrost-http/handlers/config.go
@akshaydeo
akshaydeo force-pushed the 05-20-onboarding_widget_backend_changes branch from 51e6ecd to 3da31f2 Compare May 20, 2026 05:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/configstore/rdb.go`:
- Around line 557-567: The current merge loop over `patch` only merges top-level
keys and replaces nested objects (variable `merged`), causing nested metadata
like `onboarding.dismissed` to be lost; implement a recursive helper (e.g.,
`mergeMetadataPatch(dst, patch map[string]any)`) that walks keys, deletes when
patch value is nil, and when both `dst[k]` and `patch[k]` are `map[string]any`
recurses to merge nested maps, otherwise sets `dst[k]=patch[k]`; call this
helper instead of the existing non-recursive loop to preserve nested metadata
during patching.
🪄 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: c4e778e8-9c72-434d-8cc2-0693f33fb414

📥 Commits

Reviewing files that changed from the base of the PR and between 51e6ecd and 3da31f2.

📒 Files selected for processing (10)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/clientconfig_test.go
  • framework/configstore/tables/config.go
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/lib/config_test.go
  • ui/lib/types/config.ts

Comment thread framework/configstore/rdb.go Outdated
@akshaydeo
akshaydeo force-pushed the 05-20-onboarding_widget_backend_changes branch from 3da31f2 to 135a863 Compare May 20, 2026 07:00

akshaydeo commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • May 20, 7:05 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 20, 7:05 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo merged commit 4c87d09 into dev May 20, 2026
14 of 15 checks passed
@akshaydeo
akshaydeo deleted the 05-20-onboarding_widget_backend_changes branch May 20, 2026 07:05
akshaydeo added a commit that referenced this pull request May 20, 2026
## Summary

Adds a `metadata_json` column to `config_client` that stores a free-form JSON blob of UI/admin preferences (e.g. `onboarding_dismissed`). This blob is intentionally excluded from the `ClientConfig` API struct so that `config.json` sync can never overwrite it. A new `POST /api/config/metadata` endpoint allows callers to merge patches into the blob, and `GET /api/config` now includes the metadata in its response.

## Changes

- Added `migrationAddClientConfigMetadataColumn` to create the `metadata_json` column on `config_client` with a rollback path.
- Added `MetadataJSON` (persisted) and `Metadata` (virtual) fields to `TableClientConfig`, with `BeforeSave`/`AfterFind` hooks to marshal/unmarshal the JSON blob.
- `UpdateClientConfig` now reads and preserves `MetadataJSON` inside its DELETE+CREATE transaction so config writes never clobber UI preferences.
- Added `GetClientMetadata` and `UpdateClientMetadata` to `RDBConfigStore`. `UpdateClientMetadata` performs a targeted `UPDATE` on `metadata_json` only, merging the patch and removing keys whose value is `nil`.
- Exposed both methods on the `ConfigStore` interface.
- Registered `POST /api/config/metadata` in the HTTP transport; `GET /api/config` now appends the metadata blob to the response when non-empty.
- Added `MetadataKeyOnboardingDismissed` constant as the first typed key in the metadata blob.
- Added `metadata` field to the `BifrostConfig` TypeScript type.
- Added stub implementations of `GetClientMetadata` and `UpdateClientMetadata` to `MockConfigStore` in tests.

## Type of change

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

## Affected areas

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

## How to test

```sh
# Core/Transports
go test ./framework/configstore/... ./transports/bifrost-http/...

# Verify migration runs cleanly on a fresh or existing DB by starting the server
# and confirming the metadata_json column exists on config_client.

# Set a metadata key
curl -X POST http://localhost:8080/api/config/metadata \
  -H "Content-Type: application/json" \
  -d '{"onboarding_dismissed": true}'
# Expected: {"success": true}

# Read it back
curl http://localhost:8080/api/config
# Expected: response includes "metadata": {"onboarding_dismissed": true}

# Clear a key by passing null
curl -X POST http://localhost:8080/api/config/metadata \
  -H "Content-Type: application/json" \
  -d '{"onboarding_dismissed": null}'
# Expected: {"success": true}, metadata key removed

# UI
cd ui
pnpm i
pnpm build
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

The `POST /api/config/metadata` endpoint is gated by the same middleware chain as the rest of `/api/config`, so it inherits existing auth controls. The metadata blob is a free-form store; callers should avoid writing secrets or PII into it, as it is returned in plaintext via `GET /api/config`.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@akshaydeo akshaydeo mentioned this pull request May 20, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 15, 2026
18 tasks
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