For example, a P2TR ends up unlocked with a time lock, and I infer from the Merkle proof that there are two scripts in total.
You can’t infer this for certain. You know what layer of the Merkle tree the script was taken from (in this case the first layer) but there may be scripts present on the second, third, fourth layers etc that you don’t know about. I agree that if a script is taken from the first layer it is possible (perhaps likely) there are two scripts.
Then I can guess that this is a MAST containing a hash time lock (since this is the most common script with two conditions and one of them is a time lock).
I’m not sure why you think having one leaf script with a timelock infers that another leaf script contains a hash timelock. If you are referring to Lightning, the most common channel closes are cooperative 2-of-2 closes where no alternative script paths need to be included in the output. In the case of rarer uncooperative closes it is obvious to a blockchain observer that it is an uncooperative Lightning channel close, you can’t hide this.
It seems that forcing the user to use an unbalanced Merkle Tree would also be an option (e.g. randomly initialising the coefficients and then constructing a Huffman tree)? Or maybe put in some fake leaves?
You can definitely construct the Taproot tree however you wish. You can put scripts in lower layers than they need to be so it seems to a blockchain observer there are more script paths than there actually are. Typically to save on fees you will want to put the script you are most likely to use on a higher layer of the Merkle tree but if you are more concerned with privacy you may choose not to do this.