Skip to content

Supermemory strips NUL from content, so the provider round-trip case fails against the live service #80

Description

@YellowSnnowmann

Summary

Supermemory strips the NUL character (U+0000) from content server-side, so assert_provider's round-trip-fidelity case fails against the live service. The adapter is not at fault — the API alters the content before storing it — but the conformance suite is right to fail, and the driver currently makes a promise the engine does not keep.

Found by running the suite against the real https://api.supermemory.ai rather than the double.

Evidence

Not inferred from a read-back. The creation response itself echoes the mangled value — a two-letter string sent with a NUL between the letters comes back as the two letters alone:

POST https://api.supermemory.ai/v4/memories
{"memories":[{"content":"a<NUL>b","isStatic":false,"metadata":{}}],"containerTag":"..."}

201 Created
{"documentId":"...","memories":[{"id":"...","memory":"ab", ...}]}

Three characters in, two stored. The double never modelled this, because it was written from the same documentation the adapter was — which is the general hazard rather than a criticism of the double.

The failing assertion

tinymemory-conformance/src/suite/mod.rs, the newlines case (its literal carries \n, \r\n and a NUL):

supermemory: `newlines` content was mangled
  left:  "a\nb\r\ncd"
  right: "a\nb\r\nc<NUL>d"

The suite's own comment explains why this must fail rather than be tolerated: a driver may refuse content, but silently altering it is exactly what the assertion exists to catch.

Who this does and does not affect

It does not reach OpenCompany, and saying so keeps the fix from being scoped wrongly. Its facades JSON-encode every record into content, so a NUL inside a trace, fact or chunk crosses the wire as the six ASCII characters of its JSON escape and arrives as text there is nothing to strip. Verified rather than assumed: with the provider contract failing, that same live service passes OpenCompany's full port suite — ContextStore / FactStore / MemoryStore round-trips and the (addr, label) semantics included (opencompany#1557).

It does affect any consumer that hands the driver raw text. MemoryCore::store promises that what you store is what you get back, and against Supermemory that is untrue for this character.

Options

  1. Document it. Note in the Supermemory adapter that NUL does not survive, and give the conformance case a driver-scoped exemption with the reason attached. Cheapest and honest, but it weakens a suite whose whole value is that it does not bend.
  2. Refuse it. Reject content containing NUL from the Supermemory adapter with MemoryError::Invalid. The suite already accepts a refusal — "may refuse" is its documented alternative to storing faithfully — so this passes without weakening the assertion, and the caller learns at the boundary instead of discovering it later.
  3. Encode it. Escape NUL on write and unescape on read inside the adapter. Round-trip becomes true, at the cost of content in the vendor's own UI no longer matching what the caller sent, and of a scheme every other reader of that account would have to know.

Option 2 reads best to me: it keeps the contract meaning what it says, and turns silent data loss into a refusal the caller can act on. The call belongs to whoever owns the adapter's compatibility posture.

Reproducing

OPENCOMPANY_TEST_SUPERMEMORY_URL=https://api.supermemory.ai \
OPENCOMPANY_TEST_SUPERMEMORY_KEY=... \
  cargo test --features tinymemory --lib live_hosted

from opencompany#1557, which added the live-service lane. Without the pair it skips, so an offline run is unaffected.

Related

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions