No, only one is needed. See:
https://github.com/splittingred/Quip/blob/develop/core/components/quip/index.class.php
Note how IndexManagerController extends QuipManagerController, which extends modExtraManagerController. That last class is important - modExtraManagerController - because it automatically routes you according to the $_GET['action'] variable. The QuipManagerController just provides all of our Quip-specific controllers with a few extra methods, and access to the $quip instance variable. That's all it does - and note it's abstract so it cannot be instantiated directly.
Now, in thread.class.php, found here:
https://github.com/splittingred/Quip/blob/develop/core/components/quip/controllers/thread.class.php
Note how it extends QuipManagerController - this means that the routing will automatically be handled, and this class will be loaded if $_GET['action'] is equal to "thread". So if I pass /modx/manager/?a=123&action=thread (where 123 is my modAction ID for the CMP), it will load that controller.
Similarly, if I passed 'home', it would load home.class.php, found here:
https://github.com/splittingred/Quip/blob/develop/core/components/quip/controllers/home.class.php
Furthermore, IndexManagerController is an interesting class, because you're not really ever loading it - you're just saying, hey! Don't load me! Load the 'home' action instead:
public static function getDefaultController() { return 'home'; }
This method in IndexManagerController tells MODX to not load IndexManagerController, but rather HomeManagerController (iow, it simulates $_GET['action'] being set to 'home' if $_GET['action'] isn't set).
I've updated the RTFM tutorial, and the Doodles repository to better reflect this.