List<EntityCategory> l2 = (from ec in context.EntityCategory.Include(c => c.TrackedEntity)
List<EntityCategory> l1 = (from ec in context.EntityCategory
from te in ec.TrackedEntity
from ue in te.User
where ue.UserId == user.UserId
select ec).DistinctBy(c => c.EntityCategoryId).ToList();
... l1.First().TrackedEntity will contain ALL of the tracked entities related to the given entity category. This is because the first statement loaded all of these entities into the context, and the second statement reused the objects that were created there.
So, what have we learned? We can only trust a LINQ statement to give us the data we ask for in the select statement, and we should never trust associated entity objects to even exist, much less contain what we're expecting.
I have a plan for dealing with this, which I'll detail once I get the kinks worked out.