> `add` with `pass` or `return 9` is somehow deeply irritating
Similar to others, I understand this feeling. And I would urge you to get over it.
First, technically returning 9 for a test of add(4,5) is correct. Your tests define what the function does, not the name. If you are thinking "a correct add adds all numbers", then that isn't wrong, but it is bringing your tacit world knowledge into the code without that knowledge being expressed in the code. And that's Not a Good Thing™.
It also seems to me that what you meant here was not "correct" (because it is correct), but "general". And that's one of the points of XP/TDD: as programmers we have a terrible tendency to over-generalise. Really horrible. "Write only the code to make the test pass" works against that tendency (and also has the nice gamification effect mentioned elsewhere).
The other point is when to generalise. The first answer is "not", the second is "not yet". Generalise only when you have several instances, otherwise your generalisation will, what's the technical term, suck.
Last not least is how to generalise. TDD/XP essentially say that you generalise only as a refactoring, that is a behavior-preserving transformation.
So when you add the add(10,2) test case, the really pedantic way of doing it would be to handle the extra case with an if-check, then once the additional test passes refactor the add method to handle all those cases with less code, by removing the case-handling and doing arithmetic.
What that means is that you separate the coding that gets the tests to pass from the more challenging bits of coding where you need to generalise, with the latter always under full test coverage.
It's a really nice way to make progress.
And of course: use your own judgement. If things get to tedious, it's OK to leave out steps. At some point you will probably find that you are running into problems, at that point you want to slow down and re-introduce more of the intermediate steps.