Lift customization items into a single observable model#312463
Merged
joshspicer merged 3 commits intomainfrom Apr 25, 2026
Merged
Lift customization items into a single observable model#312463joshspicer merged 3 commits intomainfrom
joshspicer merged 3 commits intomainfrom
Conversation
Removes debt where the sidebar 'Customizations' widget computed per-section counts via a parallel discovery code path (customizationCounts.ts) that diverged from the actual Customizations editor's data (e.g. when a ChatSessionCustomizationProvider or AHP customization was active). Introduces a singleton owning the per-active-IAICustomizationItemsModel harness ProviderCustomizationItemSource cache and exposing per-section IObservable<readonly IAICustomizationListItem[]>. Both the editor list widget and sidebar surfaces (per-link badges + header total) now read from the same observables, so counts cannot diverge from what the editor shows. - New: aiCustomizationItemsModel.ts (+ unit tests) - Editor list widget + management editor: consume the model via autoruns - Sidebar CustomizationLinkViewItem: single autorun over the model / IMcpService.servers / IAgentPluginService.plugins - AICustomizationShortcutsWidget header total: derived sum - Deletes customizationCounts.ts and its test - Updates fixture and AI_CUSTOMIZATIONS.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Contributor
There was a problem hiding this comment.
Pull request overview
Unifies AI Customizations item discovery/counting behind a single IAICustomizationItemsModel so the management editor’s lists and the Sessions sidebar badges/header total cannot diverge across different harness providers.
Changes:
- Introduces
AICustomizationItemsModel(delayed singleton) that owns the per-harness item-source cache and exposes per-section observables for items and counts. - Refactors the Customizations management editor + list widget and Sessions sidebar badges/header total to read from the model’s observables.
- Removes the legacy Sessions-side
customizationCounts.tscounting utilities and updates tests/fixtures/docs accordingly.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/test/browser/aiCustomization/aiCustomizationListWidget.test.ts | Updates widget disposal test strategy to stub the new items model. |
| src/vs/workbench/contrib/chat/test/browser/aiCustomization/aiCustomizationItemsModel.test.ts | Adds unit tests for the new model’s caching/refetch behavior. |
| src/vs/workbench/contrib/chat/browser/chat.contribution.ts | Ensures the items model singleton is registered/loaded. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagementEditor.ts | Replaces per-section count refresh logic with autoruns over model counts; sync setSection/refresh calls. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts | Switches the list widget to bind to per-section model observables rather than owning its own fetch pipeline. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationItemsModel.ts | New: single source of truth for harness-driven customization items and counts (with per-descriptor source cache). |
| src/vs/sessions/contrib/sessions/test/browser/customizationCounts.test.ts | Deletes tests for the removed Sessions-side counting utilities. |
| src/vs/sessions/contrib/sessions/test/browser/aiCustomizationShortcutsWidget.fixture.ts | Simplifies fixture to mock only IAICustomizationItemsModel for counts. |
| src/vs/sessions/contrib/sessions/browser/customizationsToolbar.contribution.ts | Sidebar per-link badge counts now come from the items model (plus MCP/plugins). |
| src/vs/sessions/contrib/sessions/browser/customizationCounts.ts | Deletes legacy parallel counting implementation. |
| src/vs/sessions/contrib/sessions/browser/aiCustomizationShortcutsWidget.ts | Header total now derived from model section counts + MCP + plugins. |
| src/vs/sessions/AI_CUSTOMIZATIONS.md | Updates documentation to describe the new single-path counting model. |
Copilot's findings
- Files reviewed: 12/12 changed files
- Comments generated: 3
- Make items model lazy: sections only fetch on first read, avoiding the 5x provider enumeration on construction. Source.onDidChange / harness switch / workspace change refetch only sections that have already been observed. - Cache sources by descriptor identity (not id) and prune entries whose descriptor is no longer in availableHarnesses. Fixes stale binding when an external harness re-registers under the same id. - Cache per-section count derived in the constructor (no allocation per call). - Add IAICustomizationItemsModel.whenSectionLoaded(section) so editor's setSection (now async again) can await the first keeps thefetch screenshot fixture deterministic. - Sidebar header total now sums over CUSTOMIZATION_ITEMS (the visible links) instead of every prompts-based section, so it cannot exceed the sum of per-link badges. Excludes Prompts which the sidebar does not surface. - Wrap items-model unit tests in a sub-suite so per-test teardown disposes before the leak-check teardown runs. Add coverage for lazy fetching and descriptor re-registration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The list widget now consumes IAICustomizationItemsModel; the fixture was still injecting only the underlying services and so failed to instantiate the widget. Register the real AICustomizationItemsModel (it transparently fetches through the existing prompts-service mock) and add the missing mock methods (findAgentSkills, getPromptSlashCommands, getHooks, getSkillUIIntegrations) that the model's discovery path exercises. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roblourens
approved these changes
Apr 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Removes debt where the sidebar Customizations widget (Agents app) computed per-section counts via a parallel discovery code path (
customizationCounts.ts) that diverged from the actual Customizations editor's data — e.g. when aChatSessionCustomizationProvideror AHP customization was active, the badges showed the wrong number.Approach
Introduces
IAICustomizationItemsModel— a singleton owning the per-active-harnessProviderCustomizationItemSourcecache and exposing per-sectionIObservable<readonly IAICustomizationListItem[]>. Both the editor list widget and sidebar surfaces (per-link badges + header total) read from the same observables, so counts cannot diverge from what the editor shows.Inspired by #312398.
Changes
aiCustomizationItemsModel.ts(+ unit tests) — single source of truth, registered as aDelayedsingleton.aiCustomizationListWidget.ts+aiCustomizationManagementEditor.ts: consume the model via autoruns.setSection/refreshare now sync; the per-section count autoruns replace the oldpromptsSectionCountScheduler+ 4 prompts-service event listeners.CustomizationLinkViewItem(customizationsToolbar.contribution.ts): single autorun overmodel.getCount(modelSection)/mcpService.servers/agentPluginService.plugins.ICustomizationItemConfig.promptType→modelSection.AICustomizationShortcutsWidgetheader total: onederivedsumming all section counts + MCP + plugins.customizationCounts.tsand its test.aiCustomizationShortcutsWidget.fixture.tsto mock justIAICustomizationItemsModelinstead of the prior constellation of services.AI_CUSTOMIZATIONS.md.McpServers / Plugins still use their own service observables directly — those already had a single discovery path.