@@ -520,6 +520,13 @@ describe("OrchestratorMcpService provider resolution", () => {
520520 skills : [ ] ,
521521 } ) ;
522522
523+ /** A registry whose probes report the same snapshots it already holds. */
524+ const providerRegistryLayer = ( providers : ReadonlyArray < ServerProvider > ) =>
525+ Layer . mock ( ProviderRegistry . ProviderRegistry ) ( {
526+ getProviders : Effect . succeed ( providers ) ,
527+ refreshInstance : ( ) => Effect . succeed ( providers ) ,
528+ } ) ;
529+
523530 const adapterRegistryLayer = ( instanceIds : ReadonlyArray < ProviderInstanceId > ) =>
524531 Layer . succeed (
525532 ProviderAdapterRegistry . ProviderAdapterRegistryV2 ,
@@ -920,16 +927,14 @@ describe("OrchestratorMcpService provider resolution", () => {
920927 Layer . mock ( ThreadManagementService . ThreadManagementService ) ( {
921928 getThreadRecords : ( ) => Effect . succeed ( parentProjection ( [ ] ) ) ,
922929 } ) ,
923- Layer . mock ( ProviderRegistry . ProviderRegistry ) ( {
924- getProviders : Effect . succeed ( [
925- providerSnapshot ( {
926- instanceId : codexInstanceId ,
927- driver : ProviderDriverKind . make ( "codex" ) ,
928- model : "gpt-5.4" ,
929- } ) ,
930- forkShadow ,
931- ] ) ,
932- } ) ,
930+ providerRegistryLayer ( [
931+ providerSnapshot ( {
932+ instanceId : codexInstanceId ,
933+ driver : ProviderDriverKind . make ( "codex" ) ,
934+ model : "gpt-5.4" ,
935+ } ) ,
936+ forkShadow ,
937+ ] ) ,
933938 adapterRegistryLayer ( [ codexInstanceId ] ) ,
934939 Layer . mock ( ProjectService . ProjectService ) ( { } ) ,
935940 Layer . mock ( ScheduledTaskService . ScheduledTaskService ) ( { } ) ,
@@ -964,6 +969,107 @@ describe("OrchestratorMcpService provider resolution", () => {
964969 } ) ,
965970 ) ;
966971
972+ it . effect ( "re-probes an unavailable target once before refusing it" , ( ) =>
973+ Effect . gen ( function * ( ) {
974+ const claudeInstanceId = ProviderInstanceId . make ( "claudeAgent" ) ;
975+ const claudeDriver = ProviderDriverKind . make ( "claudeAgent" ) ;
976+ const task = {
977+ id : taskId ,
978+ threadId : parentThreadId ,
979+ runId : parentRunId ,
980+ parentNodeId,
981+ origin : "app_owned" ,
982+ createdBy : "agent" ,
983+ driver : claudeDriver ,
984+ providerInstanceId : claudeInstanceId ,
985+ providerThreadId : null ,
986+ childThreadId,
987+ nativeTaskRef : null ,
988+ prompt : "Review the diff." ,
989+ title : null ,
990+ model : "claude-opus-5-5" ,
991+ status : "running" ,
992+ result : null ,
993+ startedAt : null ,
994+ completedAt : null ,
995+ } ;
996+ const healthy = providerSnapshot ( {
997+ instanceId : claudeInstanceId ,
998+ driver : claudeDriver ,
999+ model : "claude-opus-5-5" ,
1000+ } ) ;
1001+ const missingCli : ServerProvider = {
1002+ ...healthy ,
1003+ installed : false ,
1004+ status : "error" ,
1005+ message : "Claude Agent CLI (`claude`) was not found on PATH." ,
1006+ } ;
1007+ const codex = providerSnapshot ( {
1008+ instanceId : codexInstanceId ,
1009+ driver : ProviderDriverKind . make ( "codex" ) ,
1010+ model : "gpt-5.4" ,
1011+ } ) ;
1012+ // The cache still says the CLI is missing; a probe reports `cliInstalled`.
1013+ let cliInstalled = false ;
1014+ const probes = yield * Ref . make ( 0 ) ;
1015+ const dispatched = yield * Ref . make ( 0 ) ;
1016+ const dependencies = Layer . mergeAll (
1017+ NodeServices . layer ,
1018+ Layer . mock ( ThreadManagementService . ThreadManagementService ) ( {
1019+ getThreadRecords : ( threadId ) =>
1020+ Effect . succeed (
1021+ threadId === parentThreadId ? parentProjection ( [ task ] ) : childProjection ,
1022+ ) ,
1023+ dispatch : ( ) =>
1024+ Ref . update ( dispatched , ( count ) => count + 1 ) . pipe (
1025+ Effect . as ( {
1026+ sequence : 1 ,
1027+ storedEvents : [
1028+ {
1029+ sequence : 1 ,
1030+ commandId : null ,
1031+ event : { type : "subagent.updated" , payload : task } ,
1032+ } ,
1033+ ] ,
1034+ } as never ) ,
1035+ ) ,
1036+ } ) ,
1037+ Layer . mock ( ProviderRegistry . ProviderRegistry ) ( {
1038+ getProviders : Effect . succeed ( [ codex , missingCli ] ) ,
1039+ refreshInstance : ( ) =>
1040+ Ref . update ( probes , ( count ) => count + 1 ) . pipe (
1041+ Effect . as ( [ codex , cliInstalled ? healthy : missingCli ] ) ,
1042+ ) ,
1043+ } ) ,
1044+ adapterRegistryLayer ( [ codexInstanceId , claudeInstanceId ] ) ,
1045+ Layer . mock ( ScheduledTaskService . ScheduledTaskService ) ( { } ) ,
1046+ Layer . mock ( ProjectService . ProjectService ) ( { } ) ,
1047+ ) ;
1048+
1049+ yield * Effect . gen ( function * ( ) {
1050+ const service = yield * OrchestratorMcpService . OrchestratorMcpService ;
1051+ const delegate = ( clientRequestId : string ) =>
1052+ service . delegateTask ( scope , {
1053+ task : "Review the diff." ,
1054+ target : { providerInstanceId : claudeInstanceId , model : "claude-opus-5-5" } ,
1055+ mode : "async" ,
1056+ clientRequestId,
1057+ } ) ;
1058+
1059+ const error = yield * delegate ( "delegate-recheck-1" ) . pipe ( Effect . flip ) ;
1060+ assert . equal ( error . code , "provider_unavailable" ) ;
1061+ assert . equal ( yield * Ref . get ( probes ) , 1 ) ;
1062+ assert . equal ( yield * Ref . get ( dispatched ) , 0 ) ;
1063+
1064+ cliInstalled = true ;
1065+ const result = yield * delegate ( "delegate-recheck-2" ) ;
1066+ assert . equal ( result . providerInstanceId , claudeInstanceId ) ;
1067+ assert . equal ( yield * Ref . get ( probes ) , 2 ) ;
1068+ assert . equal ( yield * Ref . get ( dispatched ) , 1 ) ;
1069+ } ) . pipe ( Effect . provide ( OrchestratorMcpService . layer . pipe ( Layer . provide ( dependencies ) ) ) ) ;
1070+ } ) ,
1071+ ) ;
1072+
9671073 it . effect (
9681074 "inherits an available parent instance for driver-only targets and otherwise selects a healthy peer" ,
9691075 ( ) =>
@@ -1078,22 +1184,20 @@ describe("OrchestratorMcpService provider resolution", () => {
10781184 } as never ) ,
10791185 ) ,
10801186 } ) ,
1081- Layer . mock ( ProviderRegistry . ProviderRegistry ) ( {
1082- getProviders : Effect . succeed ( [
1083- providerSnapshot ( {
1084- instanceId : codexInstanceId ,
1085- driver,
1086- model : "gpt-5.4" ,
1087- enabled : testCase . inheritedEnabled ,
1088- } ) ,
1089- providerSnapshot ( {
1090- instanceId : codexAltInstanceId ,
1091- driver : testCase . candidateDriver ,
1092- model : "codex-alt-model" ,
1093- enabled : testCase . peerEnabled ,
1094- } ) ,
1095- ] ) ,
1096- } ) ,
1187+ providerRegistryLayer ( [
1188+ providerSnapshot ( {
1189+ instanceId : codexInstanceId ,
1190+ driver,
1191+ model : "gpt-5.4" ,
1192+ enabled : testCase . inheritedEnabled ,
1193+ } ) ,
1194+ providerSnapshot ( {
1195+ instanceId : codexAltInstanceId ,
1196+ driver : testCase . candidateDriver ,
1197+ model : "codex-alt-model" ,
1198+ enabled : testCase . peerEnabled ,
1199+ } ) ,
1200+ ] ) ,
10971201 adapterRegistryLayer ( [ codexInstanceId , codexAltInstanceId ] ) ,
10981202 Layer . mock ( ProjectService . ProjectService ) ( { } ) ,
10991203 Layer . mock ( ScheduledTaskService . ScheduledTaskService ) ( { } ) ,
0 commit comments