(Part 1 and part 2 here.)
Flat-shaded discs aren't very good for seeing rotation, so I gave them a radial pattern:
Immediately, I saw something odd: occasionally a colliding disc would start spinning, sometimes at high speed. Since collisions are currently frictionless, this should not happen.
After poking at it for an hour I pinpointed the problem. I was collecting all contacts in a list before handling any of them. However, the collision handler moves objects apart when they are interpenetrating. The contact record stores a world-space contact position, which is incorrect if the objects in question have been moved by an earlier contact resolution. In frictionless disc collisions, the collision impulse should always be aimed through the center of the disc, but due to prior movement of the disc it would sometimes be off-center, which imparted a spin to the disc.
For the moment, then, I am back to finding and handling collisions one at a time.
I'm still daunted a bit by the quadratic programming espoused by some of the papers I've been reading. I've turned up a few newer papers, and it seems like people are going in the direction I have, which is to say: integrate without worrying about constraints, then iteratively fix up problems, rather than trying to come up with a giant system of equations (or inequalities) and solve them wholesale. I don't know if that's because it's better or just easier, though.
I'm currently trying to see if I can add friction to my current model, without going to the giant-system-of-equations approach. I need friction in order to implement things like wheels. Friction is usually modeled as two different kinds: resistance to motion while in motion (dynamic friction), and resistance to motion while stationary (static friction). A rolling wheel makes use of static friction since the part touching the ground is stationary with respect to it. A skidding wheel experiences dynamic friction instead.