@@ -68,6 +68,29 @@ describe("formatPluginWarningsSummary", () => {
6868 expect ( summary ) . toBe ( "plugins: 3 skills missing: brand-identity, style, philosophy" ) ;
6969 } ) ;
7070
71+ test ( "dedupes a skill across warnings from independent collectors, not just within one" , ( ) => {
72+ // The startup path runs several separate PluginLoadDiagnostics collectors
73+ // (discovery, tool-plugins, trust, verify, add-path, profile resolution),
74+ // each with its own warnings array. If a future collector starts
75+ // reporting the same "skill missing" shape as another, whatever merges
76+ // their warnings before summarizing must still collapse to one entry per
77+ // skill — formatPluginWarningsSummary itself must not care which
78+ // collector instance a warning came from.
79+ const discoveryDiag = createPluginLoadDiagnostics ( ) ;
80+ discoveryDiag . warnings . push (
81+ 'agent a: skill "style" referenced but not found in skill search path' ,
82+ ) ;
83+ const profileResolutionDiag = createPluginLoadDiagnostics ( ) ;
84+ profileResolutionDiag . warnings . push (
85+ 'agent b: skill "style" referenced but not found in skill search path' ,
86+ 'agent b: skill "philosophy" referenced but not found in skill search path' ,
87+ ) ;
88+
89+ const merged = [ ...discoveryDiag . warnings , ...profileResolutionDiag . warnings ] ;
90+ const summary = formatPluginWarningsSummary ( merged ) ;
91+ expect ( summary ) . toBe ( "plugins: 2 skills missing: style, philosophy" ) ;
92+ } ) ;
93+
7194 test ( "mixed-warning count also counts distinct skills" , ( ) => {
7295 const summary = formatPluginWarningsSummary ( [
7396 'agent a: skill "style" referenced but not found in skill search path' ,
0 commit comments