Isn't this inferable from the code itself, though? I was expecting some subtle gotcha, so I had to read your comment three times to realise there was none!
It's to be expect that we can change a-through-&b, but not a-through-&a in foo.
I think the objection is that b may get passed throughout the program such that it's no longer local to a. Then you have 'const Foo& a' that you expect will never change, but your function reading from that calls something that mutates a 'Foo* b' that aliases the object (probably through a member variable), and suddenly you have some very hard to track down bugs. If you're multi-threaded, you have a data race.
You either need to be very strict about creating non-const references to objects, or you need to enforce immutability at the class level. Unfortunately the latter has all sorts of other practical problems, often including efficiency trade-offs or awkward APIs.
> you have 'const Foo& a' that you expect will never change, but your function reading from that calls something that mutates a 'Foo* b' that aliases the object
And this is exactly why const means pretty close to nothing. Compilers can't enforce it in so many situations that it is almost like the "auto" keyword in pre-C++11 times.
It still documents intended behavior, though. C++ is basically a giant clusterfuck if you want the compiler to actually enforce anything - because of pointer arithmetic, there's no guarantee not only that const Foo& a is immutable, but even that it points to a valid Foo or that accessing it won't crash your computer. It's trivially easy to trigger undefined behavior that may do anything up to pwning your computer, whether you make your classes immutable or not. The point of const, access control modifiers, references, static typing, RAII, and all of the other restrictions on C++ programming is to put up giant signposts that say "Don't do that! Here be dragons!" rather than actually prevent you from doing it.
If you want the compiler to actually enforce safety properties of the language, use a language like Haskell or Rust.
This seems to me a lot of trouble for "documented" behavior. It is much better to design your classes in a reasonable way and stop worrying about const issues. If you rely on const to write good code, I guarantee you that sooner or later you will be in a lot of trouble.
It's to be expect that we can change a-through-&b, but not a-through-&a in foo.