There’s a subtle distinction between truncation and censoring. In a truncated normal distribution, the process you’re describing is truncated. For example, I might be using a normal model of age, which is constrained to be positive and hence truncated below at zero. It’s physically impossible to have ages less than zero, so it’s truncation.
In a censored normal distribution, the underlying process is normal, but when you observe values beyond some point, they get reported as being beyond the threshold, but the exact values aren’t given. A typical example of censored data is actuarial. You have data that some people died, but other people you only know they have lived to their current age and will die in the future. The people who are still alive provide censored values—the only thing you know about their death age is that it’s greater than their current age.
This makes it sound like you have censored data from an untruncated distribution, rather than data from a truncated distribution.
For the observations that are censored, you either need to sample with constraints or evaluate the cdfs. I’m not sure how easy it is to sample with constraints in PyMC, but it’s always challenging to evaluate bivariate normal cdfs as you need to use some form of numerical integration. For the constrained form, you introduce a variable that is constrained to be beyond the censoring point, then give it the underlying distribution. This is super confusing terminologically, because you use a truncated normal to as the pdf. Then MCMC does the integration that the cdf would otherwise have to do.