Found during an adversarial re-review of the schema-v2 stack (#287/#288). Two low-severity robustness gaps in write_manifest_file (ordvec-manifest/src/lib.rs), which produces the bytes whose sha256 is the bundle's content address.
1. Non-atomic write. File::create truncates any existing manifest before writing — no temp-file + rename, no fsync. A crash mid-write leaves a torn manifest (fails loudly at parse/hash, not silently), and a concurrent reader can observe mixed bytes. The conventional guarantee for a content-addressed file is write-temp + atomic rename.
2. Some(NaN)/Some(inf) floats serialize as null. For the optional distortion/rate floats, serde_json emits null for a non-finite Some(x); reload gives None, and a re-write omits the key — so one logical document has two content addresses, and the NaN→null degradation is silent. Only reachable via programmatic IndexManifest construction (the create_* path never populates these) and verify already rejects the honest-NaN form, so severity is low. A finite-check in write_manifest_file (fail loud on non-finite) would uphold the content-address invariant.
Severity: low (both fail loud / require direct API misuse). Noted for hardening, not release-blocking.
Found during an adversarial re-review of the schema-v2 stack (#287/#288). Two low-severity robustness gaps in
write_manifest_file(ordvec-manifest/src/lib.rs), which produces the bytes whose sha256 is the bundle's content address.1. Non-atomic write.
File::createtruncates any existing manifest before writing — no temp-file + rename, no fsync. A crash mid-write leaves a torn manifest (fails loudly at parse/hash, not silently), and a concurrent reader can observe mixed bytes. The conventional guarantee for a content-addressed file is write-temp + atomic rename.2.
Some(NaN)/Some(inf)floats serialize asnull. For the optional distortion/rate floats,serde_jsonemitsnullfor a non-finiteSome(x); reload givesNone, and a re-write omits the key — so one logical document has two content addresses, and the NaN→null degradation is silent. Only reachable via programmaticIndexManifestconstruction (thecreate_*path never populates these) and verify already rejects the honest-NaN form, so severity is low. A finite-check inwrite_manifest_file(fail loud on non-finite) would uphold the content-address invariant.Severity: low (both fail loud / require direct API misuse). Noted for hardening, not release-blocking.