An employee at the Chennai office asks, in English, what it costs to buy
out a month of unserved notice. The number exists, it is exact, and it is in a
Tamil annexure. Searched only in English, the retriever never reaches it —
and CiteNexus does the only honest thing with what it has. It refuses.
Beforesearched in enRefused
How much must a confirmed employee at the Chennai office pay to buy out one unserved month of the notice period?
“I can’t answer that from the available evidence.”
The correct behaviour on the evidence it had — and useless to the person asking. The answer was sitting in the corpus the whole time, one script away.
cited: none · the Tamil annexure was never retrieved
Aftersearched in en, ta, teAnswered
How much must a confirmed employee at the Chennai office pay to buy out one unserved month of the notice period?
“INR 45,000”
An exact token that survives translation — the hardest cross-lingual case, because nothing about the English question looks like the Tamil passage that answers it. Reached because the English question was also searched in Tamil and Telugu.
cited to06-ta-notice-buyout-annexure·answer_language: en
One argument changed. Nothing else — same corpus, same models, same 22 English
questions:
Tamil leads this page for a measurement reason, not a decorative one. Tamil
was already a claimed script in the baseline, so these rows isolate exactly one
variable: the search fan-out. Telugu’s numbers are larger and mean less — Telugu
was absent from the tokenizer’s script table before this run, so part of its lift
is being able to tokenize the script at all. Same sitting, two changes, and a
single run cannot separate them. Tamil can.
Three more English questions, each answered from a Tamil annexure only after the
fan-out:
Asked in English
Before
After
Cited
Days of notice for the extra festival leave?
refused
குறைந்தபட்சம் 15 நாட்கள் முன்னதாக
05-ta-chennai-leave-annexure
Weeks of paid maternity leave at Chennai?
refused
26 வாரங்கள் ஊதியத்துடன் கூடிய மகப்பேறு விடுப்பு வழங்கப்படும்
07-ta-maternity-creche-annexure
Additional festival-leave days per leave year?
4 நாட்கள்
4 நாட்கள்
05-ta-chennai-leave-annexure
That last row already worked before the change, and it is on this page for that
reason. Six of the 22 questions turn on a Tamil annexure: one was answered
before, four after. At n=6 that is a direction, not a rate.
Python, and only Python: ask() and the search-language fan-out are the facade.
Go and JavaScript ship the deterministic core —
the same 14-script tokenizer and the same gate — with no facade to hang a
fan-out on. Their entry point is further down this page.
Measured over a 12-document English/Tamil/Telugu corpus, one live run per
configuration (Jina embeddings + reranker, Gemini at temperature 0):
searched in en
searched in en, ta, te
answered / abstained
8 / 14
16 / 6
cited the right document
75%
100%
answered when the corpus could ground it
44%
89%
groundedness
100%
100%
abstained when nothing could ground it
100%
100%
That last row is the whole point. It answers twice as often without
answering one thing it shouldn’t have.
14 scripts are claimed in the Python reference — Latin, Cyrillic, Greek, Han,
Hiragana, Katakana, Hangul, Arabic, Hebrew, Devanagari, Bengali, Tamil, Telugu,
Thai — each pinned by a golden conformance vector. A script that is not claimed
is reported by name and refused, before a model call is spent — the question
is tokenized, but the script is flagged as unclaimed rather than silently ranked.
Eleven scripts — Kannada, Malayalam, Gujarati, Gurmukhi, Oriya, Sinhala, Khmer,
Lao, Myanmar, Georgian and Armenian — are named in the table and deliberately
not claimed: “I cannot read this script” is a better answer than a plausible
ranking. Languages →
Which language you search in and which you answer in are two knobs
search_languages decides what gets retrieved. answer_language decides what
comes back. They are independent on purpose, and the evidence never votes on
either. Take Dutch — same Latin script, nothing exotic — asked against any corpus
you already have:
res = rag.ask("Hoeveel dagen opzegtermijn geldt er?",answer_language="auto")
res.answer # prose in Dutch — "auto" detects nl from the QUESTION
res.sources[0].passage # the citation, verbatim, in ITS language — never translated
res.sources[0].passage_language # what that passage actually is
Pass answer_language="nl" to force it unconditionally; leave it unset and you
get the configured default_answer_language, a fixed value rather than a quiet
inference. The one thing that never moves is the citation: a translated quote is
not the evidence, so it stays in its source language and
sources[*].passage_language tells you which one that is.
This is the documented behaviour of the answer-language rule, shown in Dutch —
not a measured result. There is no Dutch corpus in this repository, so there are
no Dutch numbers on this page.
Every one of these produces output where every visible signal says trust me:
verbatim text, a real document, a page, 100% groundedness. That is worse
than an obviously ungrounded answer, because there is nothing to notice. Each is
closed by a deterministic check, and each was closed because it was measured
failing first.
1. The words came from the passage — the claim didn’t follow
The faithfulness gate was set containment: every answer token appears in the
passage. Set containment is closed under reordering and deletion. Invert
the parties to a transaction and you have the same token set; drop the not from
a prohibition and you have a strict subset. Both pass.
Measured: nine adversarial answers, each false with respect to its own
cited passage, across five domains. Accepted as grounded 9 of 9 in Python,
9 of 9 in Go, 9 of 9 in JavaScript — one contract, faithfully reproduced
three times.
Now is_supported_v2 verifies per atomic claim, with ordered gapped
containment and a polarity guard, so an answer that reuses the passage’s
vocabulary to assert the opposite is rejected. Unsupported claims are dropped
— unsupported_claims_removed counts them — and the answer keeps what survives
rather than collapsing whole. False rejection on a legitimate-support control
set: 0 of 30. The nine attacks, the predicate that replaced set containment,
and the pinned gap budget: The faithfulness gate →
2. Two sources disagree, and rank order quietly picks one
When the corpus contradicts itself, a ranked list still returns something. The
top hit wins, and you never learn there was a second answer.
CiteNexus detects conflict deterministically and surfaces it — it never
resolves it. Resolution is a policy question that belongs to you. In strict
mode it abstains and cites both sides, verbatim, in Result.sources.
Measured:0 false conflicts on 27 hard negatives written to look like
conflicts and not be, 0 on 22 unrelated pairs, and 0 on a held-out set of 10
written after the thresholds were frozen. Recall is 0.889 — this detector
is deliberately precise, not exhaustive.
It is deterministic set arithmetic, so it is native in Python, Go and
JavaScript alike, pinned by 132 shared vectors. On a non-Latin corpus only the
numeric-value rule can fire — negation and antonym run off English wordlists —
and the page below says exactly which cases that leaves uncaught.
A Florida statute answered a Texas question with a real quotation, a
correct citation, and all_claims_verified: true.
Measured on a live California landlord–tenant corpus: out-of-jurisdiction
citations 4 → 0; questions that should abstain and did, 33% → 100%.
Groundedness and citation stayed at 100% throughout — which is exactly the
point. 100% groundedness with four wrong-jurisdiction citations is the whole
reason authority had to exist.
Authority tiers are curator assertions supplied at ingest (authority={...})
— the library never infers standing from prose — and strict mode enforces a floor
that withholds grounded-but-unauthoritative evidence.
Authority →
4. The governing document is in a script the system can’t read
The Tamil case at the top of this page refuses, which is survivable. The sharper
failure is the one that answers. An employee in the Hyderabad office asks,
in English, how many days of unused leave they may carry forward. The English
handbook says “An employee may carry forward a maximum of 10 days.” — verbatim,
correctly cited, every claim through the faithfulness gate, 100% groundedness.
It is also wrong for the person asking: the binding Telugu annexure
(09-te-hyderabad-leave-annexure, clause 2.12) caps Hyderabad at
5 రోజులు and states that it overrides.
Not fabrication — the gate stops that — but a true quotation of a document that
does not govern, which is indistinguishable from a correct answer at the point
of use. A Unicode tokenizer makes the authoritative document citable; the
search_languages fan-out makes it reachable. Both are required; neither is
sufficient. (Telugu is the vivid illustration and not the measurement, for the
reason given above.)
Cross-lingual corpus →
5. A model failed, and the corpus was silently poisoned
A Go embedder that timed out on the Nth chunk had no way to say so, so it
returned a zero vector — and ingest indexed it. Nothing downstream can detect
that: retrieval just quietly stops finding a document that is provably present.
Failure is now expressible in the seam itself: the embedding contract returns an
error, and the write path independently refuses a provider that returns the wrong
number of vectors, or a vector that is empty, wrong-dimension for the run,
non-finite (NaN/±Inf) or all zeros — before anything is stored. That guard
is now in all three implementations, in the same rejection order, pinned by
48 committed vectors (conformance/cases/vector_validation.json).
Can the employee disclose confidential information?
“The employee shall not disclose confidential information.”
Verbatim from the source. Every word verifiable.
cited toemployee-nda.pdf·page 3
employee-nda.pdfAbstained
What is the capital of France?
“I can’t answer that from the available evidence.”
No passage supports it — so it refuses rather than guesses.
no grounding found · refused, not fabricated
Answers are generated at temperature 0 from retrieved passages only, quoted
verbatim, then checked claim by claim against the passage they cite. Nothing
survives that check without a document and a page behind it. Nothing
survives it at all → the single pinned refusal string.
A docs set that only lists wins is not one you can plan against.
Subject scope is an open gap. An answer can pass every guard above — grounded,
verbatim, top-authority, right jurisdiction — and still be about the wrong kind
of thing. A residential-tenancy statute answering a commercial-lease question
clears all three checks, correctly, because nothing in the cited sentence says
which tenancies it governs. CiteNexus guarantees no ungrounded claim. It does
not guarantee that a grounded, high-authority citation is on-scope. The
failure, the four signals that were measured and rejected, and the mitigations
you can apply today: Wrong subject, right source →
Numbers on this page are single live runs, not benchmarks. The models are
non-deterministic even at temperature 0; the safety metrics (groundedness,
citation, abstention, out-of-jurisdiction count) reproduced exactly across
repeated runs, while rate metrics moved by about one question. Quote the safety
metrics; treat the rates as illustrations.
One line: swap the transport. Keep the shipped client — it still
builds the request, applies headers, expands ${ENV} and parses the
response — and pass a callable that moves the bytes. An in-process ONNX
model needs no server and no socket.
Swap the transport →
One core, three languages
Every deterministic decision is byte-for-byte identical across Go,
JavaScript and Python, pinned by a shared suite of golden conformance
vectors generated from the Python reference and replayed by every port,
which must reproduce them exactly. Install →
Any document
PDF, DOCX, PPTX, XLSX, HTML, Markdown, CSV, plain text, or a URL — plus
figures via an injected vision model. Idempotent by content hash,
S3-native. Ingest →
Prove the corpus
Reconcile index against manifest to show the answers derive from exactly
the corpus that was agreed — and revoke a document with proof every byte
is gone. Regulated audit →
Score it, don't trust it
evaluate() scores groundedness, citation and expected-support against a
golden CSV. It cannot score abstention — a blank expected earns
expected-support only if the row was answered (evaluate.py:76), so a
must-refuse row scores better when the model answers it. Gate on refusals
by driving ask() and asserting Decision.refused.
Evaluate →
Navigate, don't cite
An entity graph and a distilled
wiki sit over the evidence. You browse the map — but
every hit resolves down to a real cited passage before an answer exists.