feat(agent): use footnote-style conversation citations to reduce chat clutter - #981
Merged
Conversation
ussaama
enabled auto-merge
August 10, 2026 17:20
ussaama
approved these changes
Aug 10, 2026
spashii
added a commit
that referenced
this pull request
Aug 10, 2026
The regex that removed a model-written "Sources"/"Footnotes" heading from already-persisted messages was a shim: it compensated in render code, forever, for a few rows written in the hours between #981 and this fix, and it only knew the English words. The handful of affected rows get a one-off data cleanup instead; the renderer keeps owning the header for everything written from now on.
spashii
added a commit
that referenced
this pull request
Aug 10, 2026
) ### What this fixes Follow-up to #981. On the reported chat (`57493241`, dashboard) the agentic reply showed **two headings** ("Sources" and "Footnotes"), the **superscript citation links gave no feedback** when clicked, and every source line stacked **several arrow icons** (the external-link arrow plus one ↩ back-reference per citation). ### Why it happened - The prompt told the model to write a literal "Sources" header above its footnote definitions. remark-gfm then moves the definitions into its own trailing section headed "Footnotes", so the model's header stays behind as an empty duplicate. - The renderer's "Footnotes" heading ships with an `sr-only` class, which our Tailwind build never generates (no source file uses it), so the heading that is meant to be invisible is visible. - The superscripts use fragment navigation. Every message mints the same ids (`#user-content-fn-1`), a repeated click on the same fragment does nothing, and when the footnote list is already on screen (the exact case in the reported chat, verified against the stored row in prod) nothing visibly happens at all. ### What changed - `Markdown.tsx` takes an optional footnote label and id prefix and passes them through remark-rehype: the section heading is ours now (visible, localised) and footnote ids are unique per message. Surfaces that pass no label keep the default "Footnotes" heading but hidden behind `dembrane-sr-only`, a visually-hidden class we define ourselves in `index.css` (our own name so it cannot collide with Tailwind's `sr-only` utility). - `ChatHistoryMessage.tsx` passes `Sources` (per locale, `Bronnen` in Dutch, other catalogs fall back to English until translated) plus a per-message id prefix. A superscript click scrolls to the source line and flashes it with the same cyan highlight the transcript page uses for a deep-linked chunk — feedback even when the target was already on screen, no URL rewrite, no history entries. The ↩ back-references are dropped in agentic chat: one icon per source line. - `agent.py` no longer asks the model for a header above the definitions; the renderer owns the heading. Prompt test updated to pin this. ### Already-persisted rows Exactly one row in all of production stores the old model-written header (verified by query): the beta test chat this was reported on. Agentic chat is beta; the row stays as is. No migration, no render-time compatibility code. ### Tests - `AgenticChatPanel.test.tsx`: 15 passed, including a new case pinning that footnote definition tags become rich transcript links. - `agent/tests/test_agent_tools.py`: 65 passed. - `tsc --noEmit` and `biome lint` clean; end-to-end renders verified: one "Sources" heading in chat, hidden "Footnotes" heading on label-less surfaces, unique prefixed ids. Discussion: https://dembraneworkspace.slack.com/archives/C0884QPQF6W/p1786381186184369
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
[conversation_id:...]citation tags to the bottom under a "Sources" list —echo/agent/agent.pyecho/agent/agent.pyecho/agent/agent.pyConfidence
Confidence: High. The system prompt changes explicitly direct the model to use GFM footnotes (
[^1]) and group multiple claims referencing the same conversation/chunk under unique footnotes at the end. The frontend regex (AgenticChatPanel.tsx) replaces[conversation_id:...]on the entire message text, meaning it will perfectly convert the footnote definition tags into rich links. Standard GFM footnotes are natively parsed by our existingMarkdown.tsxcomponent which usesremarkGfm. Added a unit test assertion to verify the revised system prompt.Discussion: https://dembraneworkspace.slack.com/archives/C0884QPQF6W/p1786381186184369