The fake_localization package sets the frame ID this way:

        private_nh.param("odom_frame_id", odom_frame_id_, std::string("odom"));

Is that correct, or should the default be "/odom" instead?

It's correct.  If it were /odom, tf_prefix would not work (since it would assume /odom was already resolved to be global).

The TransformBroadcaster appears to be doing the right thing (fake_localization broadcasts /odom -> /base_footprint). 

The warning:

[ WARN] [14.657000000]: Message from [/gazebo_1270159292170067578] has a non-fully-qualified frame_id [base_footprint].
Resolved locally to [/base_footprint].  This is will likely not work in multi-robot systems.  This message will only
print once.

is actually from receiving a message through a tf::MessageFilter, where the messages' header.stamp was not resolved with TransformListener::resolve().

Josh