Immutable Engine, explicit Snapshot discovery — no memoizing fact state
facts.Engine is pure configuration, immutable after New (custom fact registrations included, via options). Resolution is explicit: Discover(ctx) runs the configured resolvers and returns an immutable Snapshot — the canonical tree plus pure query/decode methods. This deliberately abandons the Ruby-era model where the engine memoized every resolved fact until Flush()/Reset(): that model makes freshness depend on call history, needs locks on every read path, and lets a long-running consumer silently monitor stale values. With snapshots, consistency-within-a-run and freshness-across-runs are both structural: both types are concurrency-safe because neither ever changes. Single-fact lookups are served by scoping discovery (e.g. restrict a Discover to a subtree), never by a hidden memo.
Do not re-add result caching to the Engine “for performance” — callers who want memoization hold onto their Snapshot; that is the cache, and it is already free of locks and staleness bugs. Persistent TTL caching (facter.conf semantics for the CLI) remains a discovery-time concern configured per engine, not engine-resident result state.