DriftReportRowMapper

Translate drift reports to and from the property maps the Neo4j graph store reads and writes.

Neo4j properties are scalars and flat arrays, while a report's drifted type sets are collections, so both sets are serialized to JSON strings. JSON also handles names containing pipes, tabs, newlines and quotes, which these names routinely do: they come out of LLM extraction. Both sets are written sorted, so re-saving one observation writes byte-identical JSON and the MERGE is a no-op. Nothing reads the order back; the sets are read into a Set.

The capture instant is written three ways. capturedAt is the ISO-8601 string and is half the natural key, so it is what round trips. capturedAtEpochSecond and capturedAtNano let the database sort and range-filter on the instant at full precision. Epoch milliseconds, which a version stamp uses, truncate: two reports captured 500µs apart would compare equal, leaving "newest first" arbitrary between them, and a since bound falling inside a millisecond would sweep in reports captured just before it. Sorting on the ISO string has its own failure — Instant.toString() writes no fraction on a whole second and 'Z' outranks '.', so 12:00:00Z sorts after 12:00:00.500Z.

A fourth property, contextKey, encodes the report's scope; see GLOBAL_CONTEXT_KEY.

Reads are strict: a property this mapper wrote must be present when it is read again. contextId is the one exception, because its absence is how a global report is encoded. A node missing anything else is corrupt, so the accessor throws and the store's surrounding guard skips the row with a warning.

Properties

Link copied to clipboard

The stand-in contextKey a global (unscoped) report carries.

Functions

Link copied to clipboard
fun bindMap(report: <Error class: unknown class>): Map<String, Any?>

Bind values for a write. The natural key is (schemaName, versionHash, capturedAt, contextKey).

Link copied to clipboard
fun contextKeyFor(contextId: <Error class: unknown class>?): String

The MERGE key a report's scope contributes: global, or ctx: followed by the context id.

Link copied to clipboard
fun fromRow(row: Map<*, *>): <Error class: unknown class>

Rebuild a DriftReport from a returned node's property map, checking on the way that the two halves of its scope still agree.