@@ -161,6 +161,11 @@ public function handle(HttpKernel $kernel): int
161161 ],
162162 'fixture_ids ' => $ fixtures ,
163163 ];
164+ $ evidence ['operator_surface_matrix ' ] = $ this ->operatorSurfaceMatrix (
165+ $ tenantAEvidence ,
166+ $ tenantBEvidence ,
167+ $ unscopedAuthority ,
168+ );
164169
165170 $ scenarioPassed = $ this ->waterlineEvidencePassed ($ evidence );
166171 $ waterlineScenario = [
@@ -1021,6 +1026,140 @@ private function unscopedDashboardViewPass(array $unscoped): bool
10211026 && data_get ($ unscoped , 'api_captures.dashboard_view.path ' ) === '/ ' ;
10221027 }
10231028
1029+ /**
1030+ * @param array<string, mixed> $tenantA
1031+ * @param array<string, mixed> $tenantB
1032+ * @param array<string, mixed> $unscoped
1033+ * @return array<string, mixed>
1034+ */
1035+ private function operatorSurfaceMatrix (array $ tenantA , array $ tenantB , array $ unscoped ): array
1036+ {
1037+ return [
1038+ 'tenant_scoped_surfaces ' => [
1039+ 'tenant_a ' => $ this ->tenantOperatorSurfaceVerdict ($ tenantA ),
1040+ 'tenant_b ' => $ this ->tenantOperatorSurfaceVerdict ($ tenantB ),
1041+ ],
1042+ 'unscoped_authority ' => $ this ->unscopedOperatorSurfaceVerdict ($ unscoped ),
1043+ ];
1044+ }
1045+
1046+ /**
1047+ * @param array<string, mixed> $tenant
1048+ * @return array<string, mixed>
1049+ */
1050+ private function tenantOperatorSurfaceVerdict (array $ tenant ): array
1051+ {
1052+ $ namespace = $ tenant ['namespace ' ] ?? null ;
1053+ $ namespace = is_string ($ namespace ) ? $ namespace : '' ;
1054+
1055+ return [
1056+ 'namespace ' => $ namespace ,
1057+ 'active_namespace_visible ' => $ namespace !== '' && $ this ->tenantDashboardViewPass ($ tenant , $ namespace ),
1058+ 'workflow_list_scoped ' => data_get ($ tenant , 'workflow_list.status ' ) === 200
1059+ && data_get ($ tenant , 'workflow_list.operator_scope.mode ' ) === 'namespace '
1060+ && data_get ($ tenant , 'workflow_list.operator_scope.namespace ' ) === $ namespace
1061+ && data_get ($ tenant , 'workflow_list.includes_own_run ' ) === true
1062+ && data_get ($ tenant , 'workflow_list.excludes_foreign_run ' ) === true ,
1063+ 'workflow_detail_scoped ' => data_get ($ tenant , 'workflow_detail.status ' ) === 200
1064+ && data_get ($ tenant , 'workflow_detail.operator_scope.mode ' ) === 'namespace '
1065+ && data_get ($ tenant , 'workflow_detail.operator_scope.namespace ' ) === $ namespace
1066+ && data_get ($ tenant , 'workflow_detail.namespace ' ) === $ namespace
1067+ && data_get ($ tenant , 'foreign_workflow_detail.not_found ' ) === true ,
1068+ 'schedule_list_scoped ' => data_get ($ tenant , 'schedule_list.status ' ) === 200
1069+ && data_get ($ tenant , 'schedule_list.operator_scope.mode ' ) === 'namespace '
1070+ && data_get ($ tenant , 'schedule_list.operator_scope.namespace ' ) === $ namespace
1071+ && data_get ($ tenant , 'schedule_list.includes_own_schedule ' ) === true
1072+ && data_get ($ tenant , 'schedule_list.excludes_foreign_schedule ' ) === true ,
1073+ 'schedule_detail_scoped ' => data_get ($ tenant , 'schedule_detail.status ' ) === 200
1074+ && data_get ($ tenant , 'schedule_detail.operator_scope.mode ' ) === 'namespace '
1075+ && data_get ($ tenant , 'schedule_detail.operator_scope.namespace ' ) === $ namespace
1076+ && data_get ($ tenant , 'schedule_detail.namespace ' ) === $ namespace
1077+ && data_get ($ tenant , 'foreign_schedule_detail.not_found ' ) === true ,
1078+ 'search_attribute_values_scoped ' => $ this ->captureSearchAttributePass ($ tenant , 'workflow_list ' )
1079+ && $ this ->captureSearchAttributePass ($ tenant , 'workflow_detail ' )
1080+ && $ this ->captureSearchAttributePass ($ tenant , 'schedule_list ' )
1081+ && $ this ->captureSearchAttributePass ($ tenant , 'schedule_detail ' ),
1082+ 'operator_api_scoped ' => data_get ($ tenant , 'operator_api_stats.status ' ) === 200
1083+ && data_get ($ tenant , 'operator_api_stats.operator_scope.mode ' ) === 'namespace '
1084+ && data_get ($ tenant , 'operator_api_stats.operator_scope.namespace ' ) === $ namespace
1085+ && data_get ($ tenant , 'operator_api_stats.excludes_foreign_run ' ) === true ,
1086+ 'api_captures_scoped ' => $ namespace !== '' && $ this ->tenantApiCapturesPass ($ tenant , $ namespace ),
1087+ ];
1088+ }
1089+
1090+ /**
1091+ * @param array<string, mixed> $unscoped
1092+ * @return array<string, mixed>
1093+ */
1094+ private function unscopedOperatorSurfaceVerdict (array $ unscoped ): array
1095+ {
1096+ return [
1097+ 'documented_cluster_authority ' => data_get ($ unscoped , 'documented_safe_authority ' ) === true ,
1098+ 'dashboard_cluster_authority_visible ' => $ this ->unscopedDashboardViewPass ($ unscoped ),
1099+ 'workflow_list_cluster_authority ' => data_get ($ unscoped , 'workflow_list.status ' ) === 200
1100+ && data_get ($ unscoped , 'workflow_list.operator_scope.mode ' ) === 'cluster '
1101+ && data_get ($ unscoped , 'workflow_list.operator_scope.namespace ' ) === null
1102+ && data_get ($ unscoped , 'workflow_list.includes_tenant_a_run ' ) === true
1103+ && data_get ($ unscoped , 'workflow_list.includes_tenant_b_run ' ) === true ,
1104+ 'schedule_list_cluster_authority ' => data_get ($ unscoped , 'schedule_list.status ' ) === 200
1105+ && data_get ($ unscoped , 'schedule_list.operator_scope.mode ' ) === 'cluster '
1106+ && data_get ($ unscoped , 'schedule_list.operator_scope.namespace ' ) === null
1107+ && data_get ($ unscoped , 'schedule_list.includes_tenant_a_schedule ' ) === true
1108+ && data_get ($ unscoped , 'schedule_list.includes_tenant_b_schedule ' ) === true ,
1109+ 'operator_api_cluster_authority ' => data_get ($ unscoped , 'operator_api_stats.status ' ) === 200
1110+ && data_get ($ unscoped , 'operator_api_stats.operator_scope.mode ' ) === 'cluster '
1111+ && data_get ($ unscoped , 'operator_api_stats.operator_scope.namespace ' ) === null
1112+ && data_get ($ unscoped , 'operator_api_stats.flow_count_covers_fixture_runs ' ) === true ,
1113+ ];
1114+ }
1115+
1116+ /**
1117+ * @param array<string, mixed> $matrix
1118+ */
1119+ private function operatorSurfaceMatrixPass (array $ matrix ): bool
1120+ {
1121+ foreach (['tenant_a ' , 'tenant_b ' ] as $ tenantKey ) {
1122+ $ tenant = data_get ($ matrix , 'tenant_scoped_surfaces. ' .$ tenantKey );
1123+ if (! is_array ($ tenant ) || ! is_string ($ tenant ['namespace ' ] ?? null ) || $ tenant ['namespace ' ] === '' ) {
1124+ return false ;
1125+ }
1126+
1127+ foreach ([
1128+ 'active_namespace_visible ' ,
1129+ 'workflow_list_scoped ' ,
1130+ 'workflow_detail_scoped ' ,
1131+ 'schedule_list_scoped ' ,
1132+ 'schedule_detail_scoped ' ,
1133+ 'search_attribute_values_scoped ' ,
1134+ 'operator_api_scoped ' ,
1135+ 'api_captures_scoped ' ,
1136+ ] as $ field ) {
1137+ if (($ tenant [$ field ] ?? null ) !== true ) {
1138+ return false ;
1139+ }
1140+ }
1141+ }
1142+
1143+ $ unscoped = data_get ($ matrix , 'unscoped_authority ' );
1144+ if (! is_array ($ unscoped )) {
1145+ return false ;
1146+ }
1147+
1148+ foreach ([
1149+ 'documented_cluster_authority ' ,
1150+ 'dashboard_cluster_authority_visible ' ,
1151+ 'workflow_list_cluster_authority ' ,
1152+ 'schedule_list_cluster_authority ' ,
1153+ 'operator_api_cluster_authority ' ,
1154+ ] as $ field ) {
1155+ if (($ unscoped [$ field ] ?? null ) !== true ) {
1156+ return false ;
1157+ }
1158+ }
1159+
1160+ return true ;
1161+ }
1162+
10241163 private function captureStatusAndScopePass (
10251164 mixed $ capture ,
10261165 string $ path ,
@@ -1080,6 +1219,11 @@ private function captureSearchAttributePass(array $tenant, string $evidenceKey):
10801219 */
10811220 private function waterlineEvidencePassed (array $ evidence ): bool
10821221 {
1222+ $ matrix = $ evidence ['operator_surface_matrix ' ] ?? [];
1223+ if (! is_array ($ matrix ) || ! $ this ->operatorSurfaceMatrixPass ($ matrix )) {
1224+ return false ;
1225+ }
1226+
10831227 foreach (['tenant_a_scoped_views ' , 'tenant_b_scoped_views ' ] as $ tenantKey ) {
10841228 $ tenant = $ evidence [$ tenantKey ] ?? [];
10851229 if (! is_array ($ tenant )) {
0 commit comments