Repository navigation
GRAPH RAG FUSION: name every row in the Data Plane and drop the surrogate marker and the control-plane translator #428
Description
Version / build tested against
origin/main @ d1e957217 plus the branch of PR #426.
Deployment mode
Origin — single node (local), and a three-node cluster
Engine(s) involved
Graph, Vector, Document
Summary
Follow-up to #424 and PR #426. Not a defect.
After #426, GRAPH RAG FUSION has two mechanisms that name a hit in no edge:
- The owner's Data Plane reads the hit's client identity from its stored row (
stored_client_identity) and returns it askeyinRagVectorHitandRagTextHit. The cluster coordinator renames the marked rows from it (rag_fusion/naming.rs). - The Data Plane sets a
surrogatemarker on each result that falls back to the surrogate's decimal. A control-plane translator (response_translate/rag_fusion.rs) resolves the marked rows through the answering node's catalog (resolve_surrogate_pk).
The idea: let the Data Plane name every row, then delete the marker, the translator and name_marked_rows. One mechanism, and no deny_unknown_fields coupling between the Data Plane response shape and a control-plane copy of it.
A read of the tree on the branch of PR #426 says this is feasible in part:
| Case | Today, single node | Today, cluster | After the change |
|---|---|---|---|
Row with id, a declared key, or a strict primary key that is not its decimal |
key, from the catalog | key, from the stored row | key, from the stored row |
| Key equals the surrogate's decimal | decimal | decimal | decimal |
Row minted with no id |
decimal | decimal | decimal |
No identity column, and a non-decimal key only in the catalog (native VectorInsert with a document_id, Lite vector sync push) |
key | key on the minting node, decimal elsewhere | decimal: a regression on one node |
| Bitemporal collection | key | depends on the node | decimal, unless stored_client_identity reads the versioned table |
The Data Plane never reads the catalog, so it cannot name the catalog-only rows. Net change: about 350 lines out, about 65 in, plus tests.
Proposal
Decide first whether a catalog-only row may answer its surrogate decimal. If yes:
- Add a single-node three-source test where a text-only hit in no edge names its key, and an in-process
RagStage::Localtest for two and three sources. - Name the output rows late: keep the RRF keys as they are, so scores and ranks stay identical.
- Read the versioned table in
stored_client_identityfor a bitemporal collection. - Pin the chosen catalog-only behaviour with a test.
Keying each hit by its stored identity before fusion would drop unbound entirely, but it fuses a bound vector hit with its text hit and changes scores. That needs its own change.
Expected behavior
A fusion row is named by one rule, in one place.
Actual behavior
Two mechanisms, and the second needs a marker field and a translator.
What actually happened? (check all that are true)
- Acknowledged/committed data was lost, corrupted, or silently wrong
- The server crashed, hung, or failed to start
- A security or isolation boundary was crossed
- Core functionality is broken with no acceptable workaround
- A workaround exists (rewrite the query, avoid one path, etc.)
None. This is a cleanup proposal.
Proposed severity
SEV-4 — Low: cleanup. The behaviour is correct after #426.
Reproducibility
Not applicable.
Last known-good version / commit (if a regression)
Not applicable.
Environment & logs
Linux x86_64, debug build. No log.
Before submitting
- I searched existing issues and this is not a duplicate. Related: Calvin commit refuses a predicate UPDATE or DELETE with XX000, and GRAPH RAG FUSION names a hit in no edge by its surrogate — transaction blocks and trigger bodies fail, fusion rows do not join to documents #424 (the defect), PR fix(txn,search): route predicate writes outside a block, refuse two writes of one sum balance, refuse the doc_id score alias #426 (the fix), graph: GRAPH RAG FUSION returns a surrogate number as node_id for any hit in no edge — every other search path returns the user PK #420 (closed).
- This is not a security vulnerability.
Activity
farhan-syah commentedon Oct 20, 2026
Settled by #429 in the other direction. Every search response (vector, FTS, hybrid, GRAPH RAG FUSION) names rows after fusion through one Control-Plane resolver,
resolve_surrogate_pks. It reads the local catalog in one read transaction, then asks the collection's home leader for the misses in one batched request. Catalog-only keys resolve on every node, so the regression in the table above does not occur. The fusion leg keys are typed (RagKey), so a key"7"never merges with surrogate 7. Closing.