Introduce the two KV indexes that back the cross-checker discovery
mechanism described in docs/checker-discovery.md:
dscent|{producer}|{target}|{type}|{ref} primary record
dscent-tgt|{target}|{producer}|{type}|{ref} target lookup (auto-fill)
dscobs|{producer}|{target}|{ref}|{consumer}|{k} observation lineage
dscobs-snap|{snapshotId}|... cascade on snapshot delete
ReplaceDiscoveryEntries is the canonical publication path: the whole
set previously stored for (producer, target) is cleared, then the new
set is written. The observation-lineage side uses a single upsert per
(producer, target, ref, consumer, key) tuple, with a snapshot-scoped
reverse index so deleting a snapshot cascades cleanly. Putting a ref
under a new snapshot removes the previous snap-index so a later
cascade on the old snapshot does not wipe the refreshed primary.
Adds StoredDiscoveryEntry and DiscoveryObservationRef to the host-only
model, DiscoveryEntryStorage / DiscoveryObservationStorage to the
checker usecase storage surface, embeds both in storage.Storage, and
regenerates the instrumented wrapper. Unit tests cover round-trip,
atomic replace, multi-producer aggregation, upsert, and cascade
delete.
No pipeline wiring yet.