Skip to content

V41: retrofit provider brand icons onto existing model-catalog nodes - #413

Merged
rbuergi merged 1 commit into
mainfrom
feat/retrofit-model-icons
Jul 11, 2026
Merged

rbuergi merged 1 commit into
mainfrom
feat/retrofit-model-icons

Conversation

@rbuergi

@rbuergi rbuergi commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #407 (provider brand icons). The live seeder now stamps a brand icon at create time, but existing catalog nodes are seeded ExcludeThisAndChildren, so the importer never overwrites them — a deployed catalog keeps its old Sparkle/Key icons on upgrade. This one-time DB migration backfills them.

What it does

V41_RetrofitModelCatalogIcons patches the mesh_nodes.icon column (a top-level column, not content) for LanguageModel / ModelProvider nodes, across every partition schema — the catalog has lived under Provider and Admin/Provider across versions, and users' bring-your-own provider/model nodes live in their own schemas, so iterating by node_type finds them all (also retrofits existing BYO nodes for free).

The brand is resolved by a session-local SQL function that mirrors ModelProviderIcons (model id first, then provider — so a Claude served through AzureFoundry/OpenRouter still gets the Anthropic mark). It's a deliberate point-in-time snapshot: a migration is a frozen artifact and must not depend on evolving live code.

Safety

  • Non-destructive: only rows still on a generic icon (Sparkle/Key, the sparkle.svg/key.svg defaults, or NULL) are touched — an admin's custom icon is never clobbered.
  • Idempotent: only when a brand resolves and differs from the current icon; a second run updates 0 rows.
  • Best-effort per schema (log-and-continue), mirroring V29_PinDocsForExistingUsers.

Verification

Ran the exact SQL against a throwaway Postgres 16 with representative rows:

  • claude-3-5-sonnet under provider AzureFoundry → anthropic.svg (model id wins over the gateway) ✓
  • All brands (openai/google/mistral/meta/deepseek/xai/perplexity) resolve ✓
  • Unknown provider (CustomCo) → left on Sparkle/Key ✓
  • Admin custom icon (/static/storage/...) → not clobbered ✓
  • sparkle.svg default → retrofitted ✓
  • Second run → UPDATE 0 (idempotent) ✓

What's New

Skipped — the user-facing entry shipped with #407; this is the deployment mechanism for the same feature, not a new one.

🤖 Generated with Claude Code

Follow-up to the model-icon change: the live seeder now stamps a brand icon at
create time, but existing catalog nodes are seeded ExcludeThisAndChildren so the
importer never overwrites them — a deployed catalog keeps its old Sparkle/Key
icons on upgrade. This one-time backfill patches the mesh_nodes.icon column for
LanguageModel/ModelProvider nodes across every partition schema (catalog lives
under Provider/Admin-Provider across versions; BYO nodes live in user schemas —
iterating by node_type finds them all).

Non-destructive + idempotent: only rows still on a generic icon (Sparkle/Key or
the sparkle/key SVG defaults, or NULL) are touched, never an admin's custom icon;
only when a brand resolves and differs. The brand CASE mirrors ModelProviderIcons
as a frozen point-in-time snapshot (a migration must not depend on evolving live
code). Verified against a throwaway Postgres: gateway-hosted Claude → Anthropic,
unknown providers left untouched, admin custom icons preserved, second run = 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Adds a one-time DB migration (V41) to backfill provider brand icons onto already-seeded LanguageModel and ModelProvider catalog nodes, ensuring upgraded deployments display the correct branded icons rather than legacy generic Sparkle/Key defaults.

Changes:

  • Registers V41_RetrofitModelCatalogIcons in the migration runner.
  • Implements V41 migration to iterate all partition schemas and update mesh_nodes.icon only for rows still using generic icons (or NULL), using a session-local SQL resolver that mirrors ModelProviderIcons token ordering.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
memex/aspire/Memex.Database.Migration/Program.cs Adds V41 to the ordered migration list so it executes during upgrades.
memex/aspire/Memex.Database.Migration/Migrations/V41_RetrofitModelCatalogIcons.cs Performs per-schema, idempotent backfill of mesh_nodes.icon for LanguageModel/ModelProvider rows still on generic icons, using a migration-local brand resolver.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

1 042 tests  ±0   1 042 ✅ ±0   3m 4s ⏱️ -20s
   10 suites ±0       0 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

885 tests  ±0   700 ✅ ±0   2m 51s ⏱️ +17s
  8 suites ±0   185 💤 ±0 
  8 files   ±0     0 ❌ ±0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

  9 files  ± 0    9 suites  ±0   3m 6s ⏱️ -11s
905 tests  - 77  905 ✅  - 77  0 💤 ±0  0 ❌ ±0 
915 runs   - 77  915 ✅  - 77  0 💤 ±0  0 ❌ ±0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

This pull request removes 77 tests.
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/Analysis", expectedPrefix: "FutuRe/Analysis", expectedRemainder: null)
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/EuropeRe", expectedPrefix: "FutuRe/EuropeRe", expectedRemainder: null)
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/EuropeRe/LineOfBusiness", expectedPrefix: "FutuRe/EuropeRe/LineOfBusiness", expectedRemainder: null)
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/EuropeRe/Overview", expectedPrefix: "FutuRe/EuropeRe", expectedRemainder: "Overview")
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuRe_ShouldNotReturnNull
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_UnknownDeepPath_ShouldReturnNull
MeshWeaver.Persistence.Test.ConcurrentRequestsTest ‑ ConcurrentRequests_MultipleNodeTypes_AllLoadWithoutHanging
MeshWeaver.Persistence.Test.DataContextIntegrationTest ‑ GraphHub_InitializesWithConfiguration
MeshWeaver.Persistence.Test.DataContextIntegrationTest ‑ MeshNode_ChildrenAvailable_ViaPersistence
MeshWeaver.Persistence.Test.DataContextIntegrationTest ‑ Persistence_CanCreateNodeWithContent
…

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

731 tests  ±0   729 ✅ ±0   2m 9s ⏱️ ±0s
 10 suites ±0     2 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

829 tests  +83   828 ✅ +83   5m 27s ⏱️ -13s
 10 suites ± 0     1 💤 ± 0 
 10 files   ± 0     0 ❌ ± 0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

983 tests  ±0   979 ✅ ±0   6m 27s ⏱️ -1s
 11 suites ±0     4 💤 ±0 
 11 files   ±0     0 ❌ ±0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

   58 files  ±0     58 suites  ±0   23m 6s ⏱️ -25s
5 375 tests +6  5 183 ✅ +6  192 💤 ±0  0 ❌ ±0 
5 385 runs  +6  5 193 ✅ +6  192 💤 ±0  0 ❌ ±0 

Results for commit dc834e4. ± Comparison against base commit 3454a24.

This pull request removes 77 and adds 83 tests. Note that renamed tests count towards both.
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/Analysis", expectedPrefix: "FutuRe/Analysis", expectedRemainder: null)
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/EuropeRe", expectedPrefix: "FutuRe/EuropeRe", expectedRemainder: null)
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/EuropeRe/LineOfBusiness", expectedPrefix: "FutuRe/EuropeRe/LineOfBusiness", expectedRemainder: null)
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuReSubPaths_ShouldResolve(path: "FutuRe/EuropeRe/Overview", expectedPrefix: "FutuRe/EuropeRe", expectedRemainder: "Overview")
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_FutuRe_ShouldNotReturnNull
MeshWeaver.Persistence.Test.ApplicationPageResolutionTest ‑ ResolvePathAsync_UnknownDeepPath_ShouldReturnNull
MeshWeaver.Persistence.Test.ConcurrentRequestsTest ‑ ConcurrentRequests_MultipleNodeTypes_AllLoadWithoutHanging
MeshWeaver.Persistence.Test.DataContextIntegrationTest ‑ GraphHub_InitializesWithConfiguration
MeshWeaver.Persistence.Test.DataContextIntegrationTest ‑ MeshNode_ChildrenAvailable_ViaPersistence
MeshWeaver.Persistence.Test.DataContextIntegrationTest ‑ Persistence_CanCreateNodeWithContent
…
MeshWeaver.Hosting.Monolith.Test.ChangeFeedVersionGateTest ‑ ReceivedOwnerWrites_DoNotResubscribe_ButStaleAnnouncementDoes
MeshWeaver.Hosting.Monolith.Test.CompileActivityLogTest ‑ FailedCompile_FaultsActivityAndSurfacesFullDiagnostics
MeshWeaver.Hosting.Monolith.Test.CompileActivityLogTest ‑ SuccessfulCompile_PopulatesCompiledSourcesSnapshot
MeshWeaver.Hosting.Monolith.Test.CompileActivityLogTest ‑ SuccessfulCompile_ReportsActivityLogWithSourceQueriesAndMatchedPaths
MeshWeaver.Hosting.Monolith.Test.CompileFinishAndDisposeTest ‑ FailedCompile_StillReachesTerminalDisposedState_NotStuck
MeshWeaver.Hosting.Monolith.Test.CompileFinishAndDisposeTest ‑ TriggeredCompile_FinishesGeneration_AndActivityReachesTerminalDisposedState
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ ConcurrentRequests_OnFreshNodeType_BothSucceedConsistently
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ FreshCompile_TerminalStateIsComplete
MeshWeaver.Hosting.Monolith.Test.CreateOrUpdateNodeRequestTest ‑ Upsert_OnExistingTarget_PreservesIdentityFields
MeshWeaver.Hosting.Monolith.Test.CreateOrUpdateNodeRequestTest ‑ Upsert_OnExistingTarget_UpdatesViaStream_WasCreated_False
…

@rbuergi
rbuergi merged commit 6319b77 into main Jul 11, 2026
16 checks passed
@rbuergi
rbuergi deleted the feat/retrofit-model-icons branch August 5, 2026 10:45
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