This seems to be working so thanks for the provocation and the hint about recursion. :).
To reiterate, my solution for now is that submissions run some checks, then curl a pipeline start of a second phase, keyed by a local variable.
The second phase checks whether it’s the eldest running second or third phase. If not, it does nothing but exit. If so, it curls a pipeline start of the third phase.
Third phase runs longer term checks that would take too long to run on every submission. Since they run back-to-back, the third phases are essentially piggy backing all changes up to the point that the third phase starts. Once complete, the third phase checks to see whether the sha it just built is still current. if so, it exits. If not, it curls a pipeline start of a fresh second phase.
The answer to my original question is that “you can’t” in gitlab. Gitlab isn’t that sophisticated. It has many special cases for unusual cases but simple stuff like this isn’t handled as a first class feature. So instead, I’ve split my moral pipeline into two different passes or phases through the gitlab pipeline. The UI is a little confused in that there are now three different things showing up on the pipelines page, (really more like a half dozen including branch builds, nightly builds, etc), and no easy way to see that they are three different moral pipelines. But they all pass in the routine case so the only failures that should show are actual run time failures.
I wish gitlab had a way to declare a comment for a pass through the pipeline that would show up on the pipelines UI page. That would make the pipelines UI page much easier to read.