Skip to content

test(export): account for every emitted RDF quad - #835

Closed
ansonnmm wants to merge 4 commits into
deeplethe:devfrom
ansonnmm:upstream-pr-export-oracle
Closed

ansonnmm wants to merge 4 commits into
deeplethe:devfrom
ansonnmm:upstream-pr-export-oracle

Conversation

@ansonnmm

Copy link
Copy Markdown

PROBLEM

The export serializer's unit tests assert individual triples. Nothing verifies the output as a complete set — a silently dropped family, a duplicated quad, or a triple landing in a named graph would pass every existing test.

WHY THIS IS A GENERIC UTOPIA BUG OR CONTRACT GAP

Per-triple assertions cannot catch absence or duplication. A serializer contract that claims "the whole ledger" needs set-level accounting, not sampling.

FIX

Test-only change (#[cfg(test)]). An in-repo oracle independently derives the expected quad set from a synthetic fixture — without calling any emit_* function, including its own literal/leaf construction — and requires exact equality in both Turtle and JSON-LD, all in the default graph, with emitted == distinct. A declared predicate matrix registers every conditional cell per node kind (including cells only reachable through synthetic rows), a coverage test requires every declared cell exercised in both its present and absent branches, and a mutation test requires unaccounted / moved / dropped / named-graph terms to break the accounting. New ledger surfaces must register their predicates in the matrix, so the accounting stays complete as the contract grows.

REGRESSION EVIDENCE

This change is the regression capability: the_whole_export_is_accounted_for, the_accounting_rejects_unaccounted_terms, and the_fixture_exercises_every_declared_conditional_cell all pass — 35/35 rdf:: tests on the branch.

COMPATIBILITY RISK

None at runtime — cfg(test) only. The cost is maintenance: ~2.4k lines of test code that must be updated when the serializer contract grows; the declared matrix makes that required update explicit rather than silent.


Stacked on #832#833#834 — this PR's diff includes those commits until they merge.

南慶麟 added 4 commits September 20, 2026 21:46
Install a schema-level invariant: every reference an export can resolve
must join rows that live in the same knowledge base. A column foreign
key proves the target exists, not that it is the same KB's — the
exporter would otherwise mint local IRIs naming foreign rows, or
silently drop vocabulary references that resolve to nothing.

Three layers: a precondition scan that refuses the migration on a
dirty ledger, row triggers on every edge (deferred constraint triggers
on same-table self-references so COPY and multi-row inserts are judged
at commit), and kb-ownership immutability on every owned table.
The export read model moves from "open a pool connection per page" to a
caller-provided transaction: a route can now pin one REPEATABLE READ
snapshot across the preflight check, the vocabulary reads, and every
page. A mid-stream commit can no longer leak half a rule or a dangling
wasGeneratedBy into a finished file.

Integrity is checked on the rows that survive, not on a second look.
Every page query selects the referenced row's kb atomically with the
row itself; a foreign, dangling, or merged-out reference refuses the
whole export rather than minting a local IRI that names another KB's
row or silently dropping a vocabulary link. The scan covers the
provenance chain, derivation premises, edge qualifiers, vocabulary
references, and the open-statement / time-mention / binding edges the
schema has grown since — the same families migration 0070 guards on
the write side.

Read-model additions carried by the same pages: statement qualifiers,
time mentions, typed-fact sources, fact layer/phrase/validity grade,
evidence quote offsets, chunk origin metadata, entity descriptions,
document reader/time-context fields, and vocabulary updated_at.
Rules, attribute rules, document versions, chunks and evidence were
read from the same snapshot but never serialized; their facts'
prov:wasGeneratedBy, prov:wasDerivedFrom and locator references
dangled in the file. Serialize them in snapshot order so every
emitted reference resolves.

New upstream ledger surfaces serialize too: class/relation updatedAt,
entity descriptions, document reader/time-context, chunk origin
metadata, evidence quote offsets, open-statement layer/phrase/
qualifiers/time mentions, typed-fact fromStatement provenance,
validFromGrade, and evidenceOrigin.
The serializer's unit tests assert individual triples; nothing checks
the output as a set. Add an in-repo oracle that independently derives
the expected quad set from a synthetic fixture — without calling any
emit_* function — and requires exact equality in both formats, all in
the default graph, with no duplicates.

A declared predicate matrix lists every predicate each node kind may
emit, including conditional cells; a coverage test asserts every
declared cell is exercised by the fixture in both its present and
absent states, and a mutation test asserts unaccounted terms fail.
New ledger surfaces added by later migrations must register their
predicates in the matrix, keeping the accounting complete as the
contract grows.
@ansonnmm

Copy link
Copy Markdown
Author

Closing pending author's final review of the PR set — will reopen once the series is finalized.

@ansonnmm ansonnmm closed this Sep 20, 2026
@ansonnmm ansonnmm changed the title Account for every quad the export serializer emits test(export): account for every emitted RDF quad Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant