That looks great, you've got the system working and the fun part is now iterating and improving.
I agree with your vision for Tank being an active role - having depth and strategy that players can discover.
You mentioned the prompt showing the mobs focus. I think that's the right approach.
My default prompt looks like:
Health: 89%> Opponent> a runty squig (beaten-up)
View from Krumpa, group leader currently tanking a squig.
Health: 100%> Tank> Krumpa (scratched) Opponent> a runty squig (beaten-up)
View from WeirdBoy, grouped with Krumpa.
If aggro changed, Krumpa would immediately see his prompt update with a Tank entry showing WeirdBoy as tank, and WeirdBoy's prompt would no longer have a tank entry. (Perhaps it would be best to show 'YOU' under tank when grouped?)
Having a short, punchy message when aggro changes is the key - something a player can scan for and quickly take note of.
I was thinking the 'group' command could be extended to show more detailed Tanking and aggro status.
Perhaps it also shows a + or - if a player is increasing or decreasing their aggro generation. Allowing the Tank to proactively adjust tactics on the fly, instead of purely reacting.
One memory related point:
When a mob dies, or combat ends be sure to free the memory or you'll spring a leak

I've put a call to this in extract_char() just after the event list gets cleared.
Code:
void clear_threat_list(struct char_data *mob)
{
struct threat_data *t, *next;
for (t = mob->mob_specials.threat_list; t; t = next)
{
next = t->next;
free(t);
}
mob->mob_specials.threat_list = NULL;
}
I'm going to keep working on and testing my implementation, if I come across anything else I'll drop it here!