$(echo "echo 'a'; echo 'b'"), the shell sees a command substitution. And that's it. So that's going to be a simple command whose arguments are the resulting of the split+glob operator upon the expansion of the command substitution.
The shell will take the output of the
echo "echo 'a'; echo 'b'" command, in this case
echo 'a'; echo 'b'\n, remove the trailing newline characters so that becomes
echo 'a'; echo 'b', split that according the value of the
$IFS has not been changed from its default, that will be 4 words:
'b'. Each of those words will be subject to globbing. Here, none contain globbing characters, so those words will stay as they are.
So we've got 4 arguments to run a simple command. The first argument will be used to derive the command to run.
echo is built-in most shells. So the shell will call its
echo builtin with those 4 arguments.
echo ignores its first (0th) argument and outputs the other ones separated by space characters and terminated by a newline character. Some
echo implementations expand backslash escape sequences, but there's none here. So
echo will output:
'a'; echo 'b'\n
If you want to eval it, that is if you want to have that string interpreted as shell code, use
eval "echo 'a'; echo 'b'"
That's also recognised as a simple command. Because of the quotes, the shell sees two "words":
echo 'a'; echo 'b'. Again, those will make up the arguments to the command that is derived from the first.
Here the command is the shell's
eval builtin command. Again,
eval ignores its first agument. What it does is concatenate its other arguments (here there's only one) with spaces and interpret the resulting string as shell code. When interpreting
echo 'a'; echo 'b'
The shell sees an unquoted
; which delimits commands. The first one is treated as a simple command which ends up calling
echo with two arguments... etc.