Can you expand on what you mean by "a very well-developed upfront spec"? Because that doesn't sound at all like TDD as i know it.
I work on software that takes in prices for financial instruments and does calcualtions with them. initially there was one input price for everything. A while ago, a requirement came up to take in price quotes from multiple authorities, create a consensus, and use that. I had a chat with some expert colleagues about how we could do that, so i had a rough idea of what we needed. Nothing written down.
I created an empty PriceQuoteCombiner class. Then an empty PriceQuoteCombinerTest class. Then i thought, "well, what is the first thing it needs to do?". And decided "if we get a price from one authority, we should just use that". So i wrote a test that expressed that. Then made it pass. Then thought "well, what is the next thing it should do?". And so on and so forth. And today, it has tests for one authority, multiple authorities, no authorities, multiple authorities but then one sends bad data, multiple authorities and one has a suspicious jump in its price which might be correct, might not, and many more cases.
The only point at which i had anything resembling a well-developed upfront spec was when i had written a test, and that was only an upfront spec for the 1-100 lines of implementation code i was about to write.
So your mention of "a very well-developed upfront spec" makes me wonder if you weren't actually doing TDD.
No argument about testing user interfaces, though. There is no really good solution to that, as far as i know.