I asked claude, and the reason is a subtle bug. It is because we are showing the contents of the store at the end of the query. This inadvertenlty duplicates constraints in the store.
You can test this by adding the following to your code:
That is surely wrong. The duplicate_term/2 is required to avoid backtracking destroying (part of) the term. Besides, making a copy without constraints is done using copy_term_nat/2, which is way cheaper.
So, to make a copy without constraints we’ll need both duplicate_term/2 and copy_term_nat/2. If we do that though, we do not copy the constraints and these may thus be subject to backtracking. I’m not a CHR expert (or even user). Anyone with deeper insights into this matter?
This keeps the duplicate term, but prevents it being unified with store constraints when we hit the New = Templ unification, potentially (re)triggering rules.