Problem: The coins cache still exposed and tested states that production code should not create: standalone FRESH entries, non-dirty BatchWrite() cursor entries, and spent FRESH entries.
That makes the representation harder to reason about, because CCoinsCacheEntry stores a separate DIRTY bit even though dirty entries are already tracked in the linked list.
Fix: This revives and splits the relevant parts of #30673 and #33018.
The series passes freshness through SetDirty(), removes fresh-only test states, treats BatchWrite() cursor entries as dirty by construction, rejects spent FRESH entries, removes the bare SetFresh() helper, and derives dirtiness from linked-list membership.
The final commits also tighten SpendCoin() around the same cache-state contract: repeated spends of an entry already cached as spent now fail without changing cache accounting or caller-owned output, and callers must either check the result or explicitly discard it.
coinscache_sim now checks the real SpendCoin() result against its simulation model, and the no-op repeated-spend path no longer emits the utxocache:spent tracepoint because no spend occurs.