Well, Lua really shines for dynamic and generative systems. Try making dynamics graphics with polyphony in Pd and see how far you get. Things I wouldn't do _directly_ in Lua include video processing, DSP, etc. basically things with iterations over lots of data and intensive math ops. That said, I would still do them in Lua, but I would do them via C/C++ bindings that handle all of the really processing intensive work.
My basic approach to Lua is to use it as a sketching interface first and figure out how I want things to work and then once I get a feel for it, move certain sections down to C/C++. When you make this transition, it's best to generalize so that you can reuse it later. I do this for intensive operations like matrix processing, vector-ops, buffer drawing etc. First thing to do is get yourself some good vector math and quaternion bindings. I use these all the time and they save me a ton of work. I can send you a nice quaternion lib if you need it. Also, get yourself some mid-level OpenGL abstractions bound to Lua as userdata like Textures, Shaders, FBOs, etc that handle all of the dirty work under the hood and provide a clean interface to Lua.
This is essentially what I do too. I think pd/lua is interesting as you can have 3 layers of abstraction. You have more choice of when to move a piece of functionality to a different layer depending on what you are doing. I think the general rule should be keep it as high level as possible, and if you have to make it lower level make it as generic as possible (as you say).
It can be a really subtle desicion though - for example in fluxus now I'm restricting the grunt work in C++ to the critical path (for speed), and using Scheme where I need a more powerful language for the heavy lifting (eg collada import). It's hard to come up with hard and fast rules for what should be where - but if you have decent abstraction you can change your mind (move things up and down) without adversely effecting users with incompatibility.
cheers,
dave