Not an answer, more a comment with a picture:
The locally defined \ul is no longer \relaxed when copy-pasting @egreg's expl3 code from Defining commands that are scoped to a particular environment exactly, which gives this result (outside the environment = red; inside the environment = blue):

MWE
\documentclass[a4paper,12pt]{report}
\newcommand\textdescenders{pqrgym}
\usepackage{ulem}
\usepackage{contour}
\newcommand{\ul}[1]{%
\uline{\phantom{#1}}%
\llap{\contour{red}{#1}}
}
%%\usepackage{xparse}
%%
%%\ExplSyntaxOn
%%\NewDocumentCommand{\newenvcommand}{ m m } % #1 = env name, #2 = command name
%%{
%% \tl_if_exist:cF { g_envc_#1_list_tl } { \tl_new:c { g_envc_#1_list_tl } }
%% \tl_gput_right:cn { g_envc_#1_list_tl } { #2 }
%% \exp_args:Nc \newcommand { w envc_#1_\cs_to_str:N #2 }
%%}
%%\NewDocumentCommand{\checkenvcommands}{ }
%%{
%% \tl_if_exist:cT { g_envc_\use:c {@currenvir} _list_tl }
%% {
%% \tl_map_inline:cn { g_envc_\use:c {@currenvir} _list_tl }
%% {
%% \cs_set_eq:Nc ##1 { envc_\use:c {@currenvir} _\cs_to_str:N ##1 }
%% }
%% }
%%}
%%\ExplSyntaxOff
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newenvcommand}{ m m } % #1 = env name, #2 = command name
{
\cs_if_exist:cF { g_envc_#1_list_tl } { \tl_new:c { g_envc_#1_list_tl } }
\tl_gput_right:cn { g_envc_#1_list_tl } { #2 }
\exp_after:wN \newcommand \cs:w envc_#1_\cs_to_str:N #2 \cs_end:
}
\NewDocumentCommand{\checkenvcommands}{ }
{
\cs_if_exist:cT { g_envc_\use:c {@currenvir} _list_tl }
{
\tl_map_inline:cn { g_envc_\use:c {@currenvir} _list_tl }
{ \cs_set_eq:Nc ##1 { envc_\use:c {@currenvir} _\cs_to_str:N ##1 } }
}
}
\ExplSyntaxOff
%--Taking the bit below out restores normal underlining
\newenvcommand{shaded}{\ul}[1]{%
\uline{\phantom{#1}}%
\llap{\contour{blue}{#1}
% \uline{#1}\\
% \contour{blue}{#1}
}
}
\newenvironment{shaded}{\checkenvcommands}{}
%\input{the-above-code.tex}
\contourlength{0.6pt}
\begin{document}
\Huge
{\normalsize \textbackslash uline as is: }
\uline{Test outside shaded. \textdescenders}
\vspace{2ex}
{\normalsize \textbackslash ul with \texttt{contour:}}
\ul{Test outside shaded. \textdescenders}
{\normalsize environment \texttt{shaded} with local \textbackslash ul:}
\begin{shaded}
%\show\ul
\ul{Test inside shaded. \textdescenders } %--Displays white edge if I take out the \newenvcommand{shaded}{\ul}[1]{…} bit, fails when I put it back.
\end{shaded}
\end{document}
I still don't see why contour would be needed, or how it could have an effect, since the contour halo is not even reaching far enough to affect the line. But that is unrelated.
egreg's code is always interesting.
Edited to add:
Ah, I think I've worked out why the background circumstances of the question sound so bizarre to me: it's because the ulem package by default does this (which is perfectly OK):

And there are two parameters controlling the line -- thickness, via \ULthickness:

and depth, via \ULdepth:

The documentation says that default value for \ULdepth is \maxdimen, which changes with the font. So if something is (re)setting \maxdimen to a small or zero value (or no value), and/or setting \ULdepth is not an available action to push the underline back down again (for example, the command is being \relaxed by something more important (and, as yet, unspecified)), then the underline will go through the descenders, as described in the comments. Then, in turn, if a non-ulem workaround is being sought, we arrive at how contour might enter into the picture (indeed, there are old questions/answers on point).
Simple (if that is what's happened).
I thought that perhaps I might have had a different version of ulem, but, since the last update was 2011, that seems unlikely.
As a logistics choice, my inclination would be to start with ulem first, rather than begin with co-opting (an)other package(s), simply because of the implied dependencies being embedded into the maintenance of the solution by that second method. However, that is a different question, and unrelated.
Edit 2:
Contour and ulem work perfectly fine with the different colours:

If you want to use egreg's code for scoping commands inside environments, there are three things to do: (1) check for any typing errors that may have crept in when copying from 'over', wherever 'over' is - e.g. how many ds are there in the names?; (2) use egreg's code, as per the linked question is a good source; and (3) actually use the code - defining something is just the first step.
The way you have coded shows that the \ul you are using in the shaded environment is:

not the one you are intending.
Hint: identify the environment that is actually using the \checkenvcommands command.
The easiest way is to copy egreg's code from the linked question, define your environment that uses that code, and then the \ul is scoped to that environment. Once you do all those changes, the result is:

MWE:
\documentclass[a4paper,12pt]{report}
\newcommand\testdescenders{pqgrym}
\usepackage{ulem}
\usepackage{contour}
\renewcommand{\ULdepth}{1.8pt}
\contourlength{0.8pt}
\newcommand{\ul}[1]{%
\uline{\phantom{#1}}%
\llap{\contour{white}{#1}}
}
%-- Shaded environment:
\usepackage{framed,xcolor}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newenvcommand}{ m m } % #1 = env name, #2 = command name
{
\cs_if_exist:cF { g_envc_#1_list_tl } { \tl_new:c { g_envc_#1_list_tl } }
\tl_gput_right:cn { g_envc_#1_list_tl } { #2 }
\exp_after:wN \newcommand \cs:w envc_#1_\cs_to_str:N #2 \cs_end:
}
\NewDocumentCommand{\checkenvcommands}{ }
{
\cs_if_exist:cT { g_envc_\use:c {@currenvir} _list_tl }
{
\tl_map_inline:cn { g_envc_\use:c {@currenvir} _list_tl }
{ \cs_set_eq:Nc ##1 { envc_\use:c {@currenvir} _\cs_to_str:N ##1 } }
}
}
\ExplSyntaxOff
\definecolor{shadecolor}{rgb}{.6,.8,.9}
\newenvcommand{xshaded}{\ul}[1]{%
\uline{\phantom{#1}}%
\llap{\contour{shadecolor}{#1}}
}
\newenvironment{xshaded}{\checkenvcommands\begin{shaded}}{\end{shaded}}
\begin{document}
\ul{Test outside shaded \testdescenders .}
\begin{xshaded}
\ul{Test inside shaded \testdescenders .}
\end{xshaded}
\end{document}
If you want to redefine framed package's shaded environment, that should be a separate question.
=====
Edit
For reference, etoolbox package provides a way to patch existing environments, so the code simplifies to:
MWE
\documentclass[a4paper,12pt]{report}
\newcommand\testdescenders{pqgrym}
\usepackage{ulem}
\usepackage{contour}%default: copies mode; default # copies = 16
%bring the underline nearer to the baseline
\renewcommand{\ULdepth}{1.8pt}
%set a small width of the halo around the text
\contourlength{0.8pt}
%underline is drawn first, then the overlapping text
%white: assumes page colour is white
\newcommand{\ul}[1]{%
\uline{\phantom{#1}}%
\llap{\contour{white}{#1}}
}
% framed provides shaded environment:
\usepackage{framed}
\usepackage{xcolor}
\definecolor{shadecolor}{rgb}{.85,.9,.95}
%define command to define local-scope command and repoint environment to local token-list
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newenvcommand}{ m m } % #1 = env name, #2 = command name
{
\cs_if_exist:cF { g_envc_#1_list_tl } { \tl_new:c { g_envc_#1_list_tl } }
\tl_gput_right:cn { g_envc_#1_list_tl } { #2 }
\exp_after:wN \newcommand \cs:w envc_#1_\cs_to_str:N #2 \cs_end:
}
\NewDocumentCommand{\checkenvcommands}{ }
{
\cs_if_exist:cT { g_envc_\use:c {@currenvir} _list_tl }
{
\tl_map_inline:cn { g_envc_\use:c {@currenvir} _list_tl }
{ \cs_set_eq:Nc ##1 { envc_\use:c {@currenvir} _\cs_to_str:N ##1 } }
}
}
\ExplSyntaxOff
%define a local command \ul for framed's shaded environment
\newenvcommand{shaded}{\ul}[1]{%
\uline{\phantom{#1}}%
\llap{\contour{shadecolor}{#1}}
}
%patch framed's shaded environment to check for local commands
\usepackage{etoolbox}
\AtBeginEnvironment{shaded}{\checkenvcommands}
\begin{document}
\ul{Test outside shaded \testdescenders .}
\begin{shaded}
\ul{Test inside shaded \testdescenders .}
\end{shaded}
\end{document}
Unrelated: If time is a consideration, then compilable code illustrating the problem (an MWE) will more likely generate an answer sooner, in the 6 min - 6 hr range (P > 0.90), rather than the 6 day - 6 week range (P < 0.10) - or never - that a buggy set of random code snippets ever would.
However, since I had earlier (slowly) worked out how to patch contour to run in outline mode under xelatex (in effect, passing pdf literals to the output via xdvipdfmx's \special command), interest in how contour was being applied here has led to a great deal of learning and knowledge, precisely because of "a buggy set of random code snippets".
Contourputs a halo around the letters, following the contours of the glyphs, exactly for the purpose of making the text more legible when placed over an image, graph or shaded background. There is an illustration in the documentation. Default is 16 contours. Why not just do\uline{some text}? Unrelated: your code snippet is missing a}. – Cicada Nov 28 '19 at 09:36contourplaces copies of the text underneath (with\rlap) when theoutlinepackage option is not being used, using sin and cos to position them with a small circular motion offset, to get the halo effect. For multiple colours, you could perhaps insert code into its loop, making the colour a variable, dependent on (a modulo of) the iteration. – Cicada Nov 28 '19 at 10:02\begin{document} \ul{Test outside shaded.} \begin{shaded} \ul{Test inside shaded.} \end{shaded} \end{document}. I didn't think I'd have to do that to give you context. – Matt Nov 28 '19 at 12:41}was a mis-paste when I copied it over to StackExchange. Thanks for pointing that out. – Matt Nov 28 '19 at 12:43shaded, but not also an environmentshaded: add\newenvironment{shaded}{\checkenvcommands}{}to your code. : see https://tex.stackexchange.com/questions/48073/defining-commands-that-are-scoped-to-a-particular-environment (2) Doing a\show\ulinside shaded environment puts>\ul\=relaxinto the log, so the\ulexpands to nothing and the rest prints as normal. (3) Unrelated: when I use\uline{}, it goes underneath the descenders, and nothing is cut off anywhere. – Cicada Nov 29 '19 at 10:33