All in all your understanding is correct. Shells have various builtins which replicate real programs and which are often also installed as real programms. One reason to do this is performance. Builtins do not need to fork which make them faster.
while bash commands are somewhat specific to the shell syntax
I wouldn't call them 'specific to the shell syntax'. Builtins are used and behave very much like external commands. In one case it will call an external program and in the other an internal function replicating a program.
Specific to the shell syntax are things like [[ or time which areis more part of the shell syntax and are neither builtins nornot a builtin. It is especially recognizable with [[ which allows using > and other shell special characters as 'arguments' which would normally, for a builtin or external command, setup a redirection. (Note: in Shell scripts redirections are usually added after the command and its argument but this is not a requirement. The syntax allows them at any point including in between the commands arguments.)
A shell agnostic way to use the real program instead of the builtin is to use the full path e.g. /usr/bin/pwd instead of just pwd. Or depending on the shell to disable the builtin, e.g. for Bash enable -n pwd.
Also useful is the Zsh shell builtin where which is an extended version of which and shows all locations where a command is found, not only the first.
Last but not least you example with cd is an unfortunate choice. While several shell builtins also exist as external programs there are some which do not and cannot. External programs are run separate from the shell and cannot change the shells environment. cd should change the current working directory. If you implement it as external program you would only change the working directory for this external program. It cannot not change the shell running it. So cd and many other builtins have to be shell builtins.