The situation you have described, with there being versions of the code that must not go live, is precisely what branches are for, and branching is cheap. Whether you should consider the particular branch named 'master' to be special in any way, is largely a matter of preference, though if you end up with a mature product, it is quite possible that you will want to be able to cut a release from more than one branch.
Absolutely you can either designate an unstable “develop” branch and do stakeholder demos from there while keeping master clean, or you can develop on master and ship from stabilization branches. You can have any number of branches and assign any level of quality and polices to each. But you can’t avoid TODOs entirely by keeping them only on feature branches as what you’ll need is a branch with all features integrated but not necessarily shippable.
If you do stakeholder demonstration from an integrated “develop” branch and ship from master, you can have a policy of “no TODOs in master” meaning you won’t ship the TODOs. I’m not saying one must have TODOs ever in the branch-that-will-be-released but that they can be very useful and almost unavoidable to have in some integrated branch, because you may need to show a product without it necessarily being ready. Exactly what quality gates you ship with is a preference, for example specific comment tags (PERF, FIXME, TODO, ...). These are just words. In my codebase TODO means "this works but isn't optimal, so should be revisited IF the code ever has a reason for change". In other codebases it might mean "This is utterly broken and can't be shipped". Whether shipping is acceptable of course depends on which of those interpretations one uses (And using both would be a process error).