I believe stim could figure out on its own at compilation whether a detector's parity should be even, odd or non-deterministic, without needing any further information.
By backpropagating the observables being tracked by the detector through the (Clifford) circuit, you can predict detector outcomes in the absence of errors thanks to stabilizer formalism.
In your example (without the $X$ error), the two observables are $Z_0$ and $Z_1$, which backpropagates to $X_0$ and $X_0Z_1$. These two Pauli operators commutes together so the measurement outcomes are not fully random but correlated, their product is $Z_1$ which commutes with the stabilizers of the initial state $|00\rangle$ (aka $Z_0,Z_1$), so the detector outcome is deterministic. Since the product is $+Z_1$, the detector parity should be even (i.e. the product should be 1).
If you add an $X$ gate at the beginning on $q_1$, $Z_1$ will backpropagate to $-X_0Z_1$, so the product becomes $-Z_1$ differing in signs from the initial stabilizer $Z_1$. The detector parity is now deterministic and odd (i.e. the product should be -1).
If instead you consider the $X$ error always happen, the product becomes $X_0Z_1\cdot -X_0$ which is still $-Z_1$, The detector parity is still deterministic and odd (i.e. the product should be -1).
If instead you start with $|01\rangle$, the negative sign no longer appear in the product $X_0Z_1\cdot X_0$ but is present in the new initial stabilizer $-Z_1$ (replacing the old $Z_1$). The difference of signs is still there, so the detector parity is still deterministic and odd (i.e. the product should be -1).
If you start with $|0+\rangle$, the initial stabilizer are $Z_0, X_1$. The detector product anticommutes with $X_1$, so the detector outcome is non-deterministic:
import stim
c = stim.Circuit("""
RX 1
H 0
CX 0 1
X_ERROR(0.0) 0
M 0 1
DETECTOR rec[-1] rec[-2]
""")
print(c.detector_error_model())
This code fails as expected because the detector is non-deterministic. stim.Circuit.detector_error_model
by default fails on non-deterministic detectors, while c.compile_detector_sampler().sample(shots=2)
allows them, but samples 50/50 outcomes for them.
Since the product $X_0Z_1\cdot X_0$ never actually depends on $q_0$, you could start in state $|10\rangle$, $|+0\rangle$ or $|-0\rangle$ while still having a deterministically even detector parity.
In practice, I think stim does not compute whether each deterministic detector should be odd or even but only which ones are deterministic and which ones are not. This means you cannot effectively use stim detectors directly to assert whether two measurements are identical, only to assert that they are deterministic in the absence of errors (there might exist some stim functions to do this, but this is not the purpose of detectors).
It is in the sense that the detector is deterministic that the documentation states it should always be the same.
Compare these 2 circuit samplings:
import stim
stim.Circuit("""
H 0
CX 0 1
X_ERROR(0.0) 0
M 0 1
DETECTOR rec[-1] rec[-2]
""").compile_detector_sampler().sample(shots=200)
and
import stim
stim.Circuit("""
H 0
X 1
CX 0 1
X_ERROR(0.0) 0
M 0 1
DETECTOR rec[-1] rec[-2]
""").compile_detector_sampler().sample(shots=200)
Both always outputs False, despite one having a detector whose parity is even and the other one odd. Since no errors can occur, no detector flips were sampled.
Detectors are primarily used in syndrome extraction for quantum error correction, where you care more about which stabilizer operator was flipped and when than its actual measurement. Stim (and detectors) takes advantage of this to sample more efficiently circuit experiments.
For efficiency, stim never samples the actual errors that occured, but only detector flips. This is possible thanks to the DetectorErrorModel
that stim first precomputes before sampling. I recommend reading about it to understand what stim does precisely.