@@ -36,7 +36,6 @@ import {
3636 createKnowledgeAclFixtureIds ,
3737 seedKnowledgeAclFixture ,
3838} from '@/lib/knowledge/__integration__/seed-source-access-fixture'
39- import { searchScopedKnowledge } from '@/lib/knowledge/application/workspace-search'
4039import {
4140 SearchBudget ,
4241 SearchDeadlineError ,
@@ -166,6 +165,9 @@ const diagnosticSchema = z
166165 surface : z . enum ( [ 'dashboard' , 'copilot' ] ) ,
167166 outcome : z . literal ( 'success' ) ,
168167 elapsedMs : z . number ( ) ,
168+ vectorBudgetMs : z . number ( ) . positive ( ) ,
169+ retrievalStatus : z . enum ( [ 'complete' , 'partial' ] ) ,
170+ timedOutLegs : z . array ( z . enum ( [ 'vector' , 'keyword' , 'tags' ] ) ) ,
169171 toolResultBytes : z . number ( ) . int ( ) . nonnegative ( ) . optional ( ) ,
170172 passageBytes : z . number ( ) . int ( ) . nonnegative ( ) . optional ( ) ,
171173 maxPassageBytes : z . number ( ) . int ( ) . nonnegative ( ) . optional ( ) ,
@@ -217,6 +219,45 @@ async function search(
217219 )
218220}
219221
222+ async function searchDashboard ( ) {
223+ const authenticate = vi . spyOn ( internalSessionAuth , 'authenticate' ) . mockResolvedValue ( {
224+ kind : 'session' ,
225+ userId : ids . aliceId ,
226+ sessionId : 'fixture-dashboard' ,
227+ } )
228+ try {
229+ const response = await searchRoute (
230+ new NextRequest ( 'http://localhost/api/knowledge/search' , {
231+ method : 'POST' ,
232+ headers : { 'content-type' : 'application/json' } ,
233+ body : JSON . stringify ( {
234+ workspaceId : ids . workspaceId ,
235+ query : 'Orion deployment' ,
236+ topK : 15 ,
237+ } ) ,
238+ } )
239+ )
240+ expect ( response . status ) . toBe ( 200 )
241+ return {
242+ success : true as const ,
243+ data : workspaceKnowledgeSearchDataSchema . parse ( ( await response . json ( ) ) . data ) ,
244+ }
245+ } finally {
246+ authenticate . mockRestore ( )
247+ }
248+ }
249+
250+ /** Allow either index or filtered plans, but require successful retrieval within the real surface budget. */
251+ function expectCompleteVectorSearch ( diagnostics : z . infer < typeof diagnosticSchema > ) {
252+ const budget = diagnostics . surface === 'dashboard' ? 3000 : 8000
253+ expect ( diagnostics ) . toMatchObject ( {
254+ vectorBudgetMs : budget ,
255+ retrievalStatus : 'complete' ,
256+ timedOutLegs : [ ] ,
257+ } )
258+ expect ( diagnostics . stages . vector . totalMs ) . toBeLessThanOrEqual ( budget )
259+ }
260+
220261async function sample ( label : string , run : ( ) => ReturnType < typeof search > ) {
221262 captured . length = 0
222263 diagnosticLog ?. mockClear ( )
@@ -614,11 +655,6 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
614655 'returns incomplete dashboard coverage when %s SQL branches exceed their deadline' ,
615656 async ( delayedLegs ) => {
616657 diagnosticLog ?. mockClear ( )
617- const authenticate = vi . spyOn ( internalSessionAuth , 'authenticate' ) . mockResolvedValue ( {
618- kind : 'session' ,
619- userId : ids . aliceId ,
620- sessionId : 'fixture-dashboard' ,
621- } )
622658 const query = SearchBudget . prototype . query
623659 const delayed = vi . spyOn ( SearchBudget . prototype , 'query' ) . mockImplementation ( function < T > (
624660 this : SearchBudget ,
@@ -632,26 +668,14 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
632668 } ) as Promise < T >
633669 } )
634670 try {
635- const response = await searchRoute (
636- new NextRequest ( 'http://localhost/api/knowledge/search' , {
637- method : 'POST' ,
638- headers : { 'content-type' : 'application/json' } ,
639- body : JSON . stringify ( {
640- workspaceId : ids . workspaceId ,
641- query : 'Orion deployment' ,
642- topK : 15 ,
643- } ) ,
644- } )
645- )
646- expect ( response . status ) . toBe ( 200 )
671+ const { data } = await searchDashboard ( )
647672 const completed = diagnosticLog ?. mock . calls . find (
648673 ( [ message ] ) => message === 'Knowledge search completed'
649674 )
650675 const diagnostics = diagnosticSchema . parse ( completed ?. [ 1 ] )
651676 expect ( diagnostics . vectorBudgetMs ) . toBe ( 3000 )
652677 expect ( diagnostics . stages . vector . totalMs ) . toBeGreaterThan ( 2500 )
653678 expect ( diagnostics . stages . vector . totalMs ) . toBeLessThan ( 4000 )
654- const data = workspaceKnowledgeSearchDataSchema . parse ( ( await response . json ( ) ) . data )
655679 expect ( data . retrieval ) . toEqual ( {
656680 status : 'partial' ,
657681 timedOutLegs : delayedLegs === 'both' ? [ 'vector' , 'keyword' ] : [ 'vector' ] ,
@@ -666,7 +690,6 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
666690 report [ `dashboard.deadline.${ delayedLegs } ` ] = { resultCount : data . results . length }
667691 } finally {
668692 delayed . mockRestore ( )
669- authenticate . mockRestore ( )
670693 }
671694 } ,
672695 30_000
@@ -675,7 +698,8 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
675698 it ( 'records first and repeated application searches with the actual SQL plans' , async ( ) => {
676699 const before = embeddingCalls
677700 for ( let iteration = 0 ; iteration < 2 ; iteration ++ ) {
678- const { result, plans } = await sample ( `broad.${ iteration } ` , ( ) => search ( ) )
701+ const { result, plans, diagnostics } = await sample ( `broad.${ iteration } ` , ( ) => search ( ) )
702+ expectCompleteVectorSearch ( diagnostics )
679703 expect ( result . data . results ) . toHaveLength ( 15 )
680704 expect ( result . data . results . every ( ( row ) => row . knowledgeBaseId === ids . knowledgeBaseId ) ) . toBe (
681705 true
@@ -703,9 +727,10 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
703727
704728 it ( 'preserves exact-neighbor recall across different query vectors' , async ( ) => {
705729 for ( const topic of [ 3 , 11 , 23 ] ) {
706- const { plans } = await sample ( `topic.${ topic } ` , ( ) =>
730+ const { plans, diagnostics } = await sample ( `topic.${ topic } ` , ( ) =>
707731 search ( ids . aliceId , `Topic ${ topic } deployment` )
708732 )
733+ expectCompleteVectorSearch ( diagnostics )
709734 const candidates = plans . find ( ( plan ) => plan . kind === 'vector' ) !
710735 assertCompactCandidates ( candidates . plan [ 0 ] . Plan )
711736 const rerank = plans . find ( ( plan ) => plan . kind === 'rerank' ) !
@@ -723,19 +748,10 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
723748 } , 180_000 )
724749
725750 it ( 'compares the Search tab and Assistant with the same person, query and index' , async ( ) => {
726- const dashboard = await sample ( 'dashboard' , async ( ) => {
727- const result = await searchScopedKnowledge . execute ( {
728- principal : { kind : 'session' , userId : ids . aliceId , sessionId : 'fixture-dashboard' } ,
729- input : {
730- workspaceId : ids . workspaceId ,
731- query : 'Orion deployment' ,
732- topK : 15 ,
733- surface : 'dashboard' ,
734- } ,
735- } )
736- return resultSchema . parse ( { success : true , data : result } )
737- } )
751+ const dashboard = await sample ( 'dashboard' , searchDashboard )
738752 const assistant = await sample ( 'assistant.comparison' , ( ) => search ( ) )
753+ expectCompleteVectorSearch ( dashboard . diagnostics )
754+ expectCompleteVectorSearch ( assistant . diagnostics )
739755 expect ( dashboard . diagnostics . surface ) . toBe ( 'dashboard' )
740756 expect ( assistant . diagnostics . surface ) . toBe ( 'copilot' )
741757 expect ( dashboard . diagnostics . stages . result_provenance ) . toBeUndefined ( )
@@ -767,21 +783,9 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
767783 for ( const surface of [ 'copilot' , 'dashboard' ] as const ) {
768784 const { result, plans, diagnostics } = await sample (
769785 `filtered-neighborhood.${ surface } ` ,
770- async ( ) => {
771- if ( surface === 'copilot' ) return search ( )
772- const data = await searchScopedKnowledge . execute ( {
773- principal : { kind : 'session' , userId : ids . aliceId , sessionId : 'fixture-dashboard' } ,
774- input : {
775- workspaceId : ids . workspaceId ,
776- query : 'Orion deployment' ,
777- topK : 15 ,
778- surface,
779- } ,
780- } )
781- return resultSchema . parse ( { success : true , data } )
782- }
786+ surface === 'copilot' ? ( ) => search ( ) : searchDashboard
783787 )
784- expect ( diagnostics ) . toMatchObject ( { retrievalStatus : 'complete' , timedOutLegs : [ ] } )
788+ expectCompleteVectorSearch ( diagnostics )
785789 expect ( result . data . results ) . toHaveLength ( 15 )
786790 const rerank = plans . find ( ( plan ) => plan . kind === 'rerank' ) !
787791 expect ( rerank ) . toBeDefined ( )
@@ -852,6 +856,7 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
852856 } , 180_000 )
853857
854858 it ( 'runs two independent Assistant searches concurrently' , async ( ) => {
859+ diagnosticLog ?. mockClear ( )
855860 const start = performance . now ( )
856861 const results = await Promise . all ( [ search ( ) , search ( ids . aliceId , 'Engineering operations' ) ] )
857862 report . concurrent = {
@@ -860,6 +865,12 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
860865 }
861866 saveReport ( )
862867 for ( const result of results ) expect ( result . data . results ) . toHaveLength ( 15 )
868+ const completed = diagnosticLog ! . mock . calls . filter (
869+ ( [ message ] ) => message === 'Knowledge search completed'
870+ )
871+ expect ( completed ) . toHaveLength ( 2 )
872+ for ( const [ , metadata ] of completed )
873+ expectCompleteVectorSearch ( diagnosticSchema . parse ( metadata ) )
863874 } , 180_000 )
864875
865876 it ( 'checks live reader access on every search, including after revocation' , async ( ) => {
0 commit comments