Skip to content

0011 The Sources list only ever shows what the answer actually cited

Status

Accepted

Context

A retrieved source can survive scoring, reranking, and the entity cap without ever being cited in the generated text — the model may ground its reasoning in a chunk without quoting it, or ignore a source entirely. Surfacing every retrieved-and-above-threshold source in the client's Sources list regardless produced answers where, for example, [1] and [3] were cited in the text but [2] still appeared in the Sources list, unlinked to anything.

On the SSE path this is compounded by ADR 0005: the references event fires before generation completes, so it can only ever list every retrieved source — it cannot yet know which ones the text will end up citing.

Decision

AnswerService::finalizeCitations() runs after generation completes, on both delivery paths. It drops any source with no surviving [n] marker in the text and renumbers the remainder in first-citation order (so [1][3] becomes [1][2]), rewriting the markers to match. It falls back to the untouched list only when the text cites nothing at all, so a provider that ignores the citation contract doesn't leave a grounded answer with zero sources.

On the JSON path this is the only Sources list the client ever sees. On the SSE path, the early references event still lists every retrieved source (for responsiveness — the client can render something before generation finishes); the done event separately carries the corrected text and references pair, and ai_answers.answer.js swaps to that corrected pair once done arrives.

Consequences

  • Two different reference lists exist in flight on the SSE path — the early, uncorrected one and the final, corrected one — and any front-end change to reference rendering has to account for both events, not just references.
  • stripOutOfRangeMarkers() must run before finalizeCitations(): it clears out-of-range [n] markers first so finalizeCitations()'s citation count only ever sees valid 1-based indices into $sourcesUsed.