I very much like the decision to hide implementation-based
hash functions by allowing the implementation to ignore the
user-provided hash function in certain situations.  I think this
should in fact be simplified:

    The implementation may substitute a more efficient hash function for
    any user-specified hash function, if it can prove that the substitute
    is valid for the equality predicate as described above.  Specifically,
    this allows the use of addresses as hashes, in which case the keys
    must be rehashed if they are moved by the garbage collector.

Hash functions need not be idempotent - in fact, they can't
be if the keys are not integers.

There's no discussion of the trade-offs involved in perfect
hashes.  The requirements seem to forbid them by requiring
amortized O(1) on mutation, but if that were relaxed we could
have guaranteed (non-amortized) O(1) on access.  It's probably
better to leave this to a separate data-structure to support the
frequent update case, but I thought I'd mention this.

I'm not fond of the name hash-table-extend! as the common
case seems to be not to extend, but I realize this has precedence
in Racket and don't have great alternative suggestions.  Maybe
hash-table-ensure! or hash-table-cache!.

It doesn't seem to be useful to omit failure in hash-table-extend!.
Either way, the description is hard to read.

If we're going to have -push! and -pop! conveniences, I think
-inc! and -dec! should be there too.  I use them much more
often.

Bimaps are useful but perhaps an order of magnitude less
so than hash tables - I'd rather these be in a separate library.
It's probably fine to keep them in the same SRFI and specify
a factoring, perhaps:

  (srfi 125): everything
  (srfi 125 base): constructors, predicates, accessors, mutators
  (srfi 125 iterate): whole table, mapping
  (srfi 125 bimaps)

-- 
Alex