It turns out the gl calls in the destructor were, actually, the problem. Once I moved all the appropriate glDelete calls inside their own Cleanup() function, and called that at the right time, the issue was resolved. Thanks everyone.
And I'm not sure if every vec3 input is converted to a vec4 implicitly, but it seems like it would - after all, the shader doesn't know if the first vec3 you're passing to it is position or color data or something else.
It's good to hear your problem is resloved. I put a macro after every OpenGL call that resolves to a check on current context and OpenGL error check in debug mode and nothing in release. It makes the code run a bit slower but helps catch some of these things.
I am looking forward to the callback error code working the new drivers.