A good step is awareness for users and invalidating the cache via project-forget methods is a good idea. I'd also offer a direct function to invoke to invalidate the cache for programmatic use.
vc caching, longer term, may need to consider a few more complex use cases such as git repos with both submodules that are considered extensions of the base project and submodules which are not. A concrete example I see often is a "mono repo" structure with core server and library code but with web and mobile front end code in submodules that are treated as part of the project proper BUT with submodules for vendor/third-party code that are not. A question here would be which parts of the tree belong to which cached vc root.
Another use case I see is working on many unrelated projects/repos across a variety of clients all in the same Emacs session and with perhaps 100+ buffers/files open (as I pretty much have right now), a 17-element cache won't be sufficient? Should the cache be for parent directories and not for file names? With files, it gets full fast. Mine is full right now with files most of which share the same repo root and some that don't. I have wondered whether an implementation would be better as directory variables? Cache invalidation without timestamps on .dir-locals.el files remain the same but directory variable treatment might be more natural to Emacs users?
-Stephane