Currently you are using SCMHead.HeadByItem.findHead(item) instanceof ChangeRequestSCMHead which is reasonable, but not the intent of the API.
The intent of the API is that you would ask the item's parent SCMSource for the SCMHeadCategory though I should point out that if the code in question is the hot path, that check would be more expensive in terms of CPU.
You probably need to use a profiler and see where the code is hot.
To use categories correctly, you need to ask the relevant SCMSource instances for the categories they are providing. Once you have all the potential categories, you then use SCMHeadCategory.collect to aggregate them... Each of those aggregate categories should get its own "tab" (you can consider treating the "uncategorized" category specially).
Then for each tab, you just ask it's category if the SCMHead is a SCMHeadCategory.isMatch(SCMHead)
Attached: Me first navigating from home page to Core/jenkins activity page, then to PRs. Takes 1.2 minutes.