Moved out of the viewer into @ifc-lite/cache (#5138 PR 7b review) so a
headless caller (the CLI, the MCP server) can compute the SAME
EvaluatorModel.filterIdentity the viewer stores as
FederatedModel.sourceFingerprint — a rule set's targets.modelFingerprints
only resolves against a model when both sides hash it identically.
Spread-sampled source fingerprint — a fast, strong cache KEY, NOT a full
content validation. buildGeometryCacheKey (viewer, hooks/geometryCacheKey.ts)
folds SourceFingerprint.hex into the key so the RIGHT entry is
looked up; it replaces the old
first-4KB+last-4KB FNV-32 fingerprint (32 bits, interior-blind) with a wider
64-bit hash over a head/tail/interior spread plus the exact byte length.
O(1) sampler — and its deliberate blind spot
It touches a FIXED ~160KB spread regardless of file size (head HEAD_TAIL_BYTES + tail HEAD_TAIL_BYTES + INTERIOR_WINDOWS
interior windows + the 8-byte length), so it is sub-millisecond even on a
400MB file. That O(1) speed is exactly why it CANNOT be the validation: it
never reads the bytes BETWEEN its sample windows. A byte-length-preserving
in-place edit that lands in a gap — a GUID patch (GUIDs are a fixed 22 chars),
a scripted same-width coordinate/string edit — produces an IDENTICAL
fingerprint. For the source-PERSISTING tier that is harmless (it serves cached
geometry AND cached source together — self-consistent), but for the
source-DECOUPLED (mesh-only) tier, which hydrates cached geometry against the
FRESH buffer, an unvalidated gap-edit hit would be a silent chimera (old
geometry + new properties).
Validation lives elsewhere — NOT in this fingerprint
A mesh-only hit is validated by the source File's lastModified (mtime guard:
any real on-disk edit bumps it → safe miss) plus a TRUE full-file content hash
(SHA-256, computed off the main thread and re-checked in the background to
catch the deliberate mtime-preserving gap edit → purge + reload). See the
viewer's hooks/cacheTier.tsdecideMeshOnlyCacheHit, utils/sourceContentHash.ts,
and hooks/useIfcLoader.ts. This fingerprint's only job is to key the
lookup cheaply and make a false key-hit (a genuinely different file
mapping to the same entry) astronomically rare, so the mtime / full-hash
gate almost never has to reject one. It is still strictly stronger than
the old weak key (proved in source-fingerprint.test.ts), but strength
of the KEY is a performance property, not the safety guarantee.
Moved out of the viewer into
@ifc-lite/cache(#5138 PR 7b review) so a headless caller (the CLI, the MCP server) can compute the SAMEEvaluatorModel.filterIdentitythe viewer stores asFederatedModel.sourceFingerprint— a rule set'stargets.modelFingerprintsonly resolves against a model when both sides hash it identically.Spread-sampled source fingerprint — a fast, strong cache KEY, NOT a full content validation.
buildGeometryCacheKey(viewer,hooks/geometryCacheKey.ts) folds SourceFingerprint.hex into the key so the RIGHT entry is looked up; it replaces the old first-4KB+last-4KB FNV-32 fingerprint (32 bits, interior-blind) with a wider 64-bit hash over a head/tail/interior spread plus the exact byte length.O(1) sampler — and its deliberate blind spot
It touches a FIXED ~160KB spread regardless of file size (head HEAD_TAIL_BYTES + tail HEAD_TAIL_BYTES + INTERIOR_WINDOWS interior windows + the 8-byte length), so it is sub-millisecond even on a 400MB file. That O(1) speed is exactly why it CANNOT be the validation: it never reads the bytes BETWEEN its sample windows. A byte-length-preserving in-place edit that lands in a gap — a GUID patch (GUIDs are a fixed 22 chars), a scripted same-width coordinate/string edit — produces an IDENTICAL fingerprint. For the source-PERSISTING tier that is harmless (it serves cached geometry AND cached source together — self-consistent), but for the source-DECOUPLED (mesh-only) tier, which hydrates cached geometry against the FRESH buffer, an unvalidated gap-edit hit would be a silent chimera (old geometry + new properties).
Validation lives elsewhere — NOT in this fingerprint
A mesh-only hit is validated by the source File's
lastModified(mtime guard: any real on-disk edit bumps it → safe miss) plus a TRUE full-file content hash (SHA-256, computed off the main thread and re-checked in the background to catch the deliberate mtime-preserving gap edit → purge + reload). See the viewer'shooks/cacheTier.tsdecideMeshOnlyCacheHit,utils/sourceContentHash.ts, andhooks/useIfcLoader.ts. This fingerprint's only job is to key the lookup cheaply and make a false key-hit (a genuinely different file mapping to the same entry) astronomically rare, so the mtime / full-hash gate almost never has to reject one. It is still strictly stronger than the old weak key (proved insource-fingerprint.test.ts), but strength of the KEY is a performance property, not the safety guarantee.