Skip to content

AI citations / source attribution

ai-ui specs/ai-ui/citations.kmd

Footnote-style inline citations + hover/long-press preview cards + source list sidebar for AI responses backed by RAG or web search. Mandatory in any RAG/search-backed surface; compliance basis for EU AI Act transparency requirements on source disclosure.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — AI citations / source attribution

RAG/search-backed AI responses MUST attribute sources. Sem attribution: viola transparency principle (policies/security.kmd adjacente), EU AI Act art. 50 (high-risk applications), e LGPD art. 9. Consumer principal: chat-message-bubble.kmd R5 (multi-modal content).

Princípios

  1. Inline + sidebar dual — cada claim com superscript [N]; sources list completa em sidebar/footer.
  2. Click reveals source — não exige scroll: tap inline → preview card.
  3. Confidence transparente — quando RAG fornece relevance score, expose.
  4. Copy preserves citations — copy-to-clipboard mantém footnote refs.
  5. Workspace-scoped sources — RAG search scope obedece tenant boundaries.

R1 — Inline citation

Render: superscript [1] [2] em pontos onde claim is backed by source.

São Paulo has 12 million residents[1] and is the largest city in Brazil[2].

Visual:

  • Superscript vertical-align: super; font-size: 75%.
  • Color: accent (per themes/color-roles.kmd).
  • Cursor: pointer.
  • Hover (desktop) / long-press (mobile): reveal preview card (R3).
  • Click: scroll-to-source in sidebar OR open in new surface.

Inline placement is gateway-provided: response includes citations array with offsets, NOT inline-spliced by client.

R2 — Citation data model

Gateway response shape:

{
  "content": [
    {"type": "text", "text": "São Paulo has 12 million residents and is the largest city in Brazil."}
  ],
  "citations": [
    {
      "id": "1",
      "offsets": [{"text_index": 0, "start": 17, "end": 39}],
      "source": {
        "type": "web" | "doc" | "memory",
        "url": "https://...",  // for web
        "doc_id": "...",       // for doc
        "memory_id": "...",    // for memory
        "title": "São Paulo - Population statistics",
        "snippet": "...",
        "favicon": "https://...",
        "confidence": 0.92,    // 0.0–1.0
        "fetched_at": "2026-05-14T12:00:00Z"
      }
    },
    ...
  ]
}

Client renders superscript at each offsets[].startend range, linked to source by id.

R3 — Preview card (hover/long-press)

Reveal trigger:

  • Desktop: hover 300ms.
  • Mobile: long-press.
  • Keyboard: focus → Enter or Space.

Card content:

┌─────────────────────────────────────────────┐
│  [favicon] Source title                     │
│  weather.com/sao-paulo · 2 hours ago        │
├─────────────────────────────────────────────┤
│  "São Paulo is the largest city in Brazil   │
│  with over 12 million residents..."         │
├─────────────────────────────────────────────┤
│  Confidence: 92% · [Open source ↗]          │
└─────────────────────────────────────────────┘
  • Positioning: per koder_kit tooltip primitive; auto-flip se overflow.
  • Max width: 380dp (desktop) / 90% viewport (mobile).
  • Z-index: above bubble; below modals.
  • Dismiss: mouseout (desktop) / tap outside (mobile) / Escape.

R4 — Sources list (sidebar/footer)

Per chat-message-bubble (R5): citation_list rendered como content item no fim do bubble OU em sidebar lateral.

Footer mode (default):

— Sources
[1] weather.com/sao-paulo · São Paulo - Population statistics · 92%
[2] ibge.gov.br/cidades · IBGE 2024 census · 88%

Sidebar mode (Kortex research surface):

┌─ Sources (2) ──────────────────┐
│ 1 ▸ weather.com                 │
│    São Paulo Population        │
│    Confidence: 92%             │
│                                 │
│ 2 ▸ ibge.gov.br                 │
│    IBGE 2024 census            │
│    Confidence: 88%             │
└─────────────────────────────────┘

Sortable: por confidence desc (default), recency, alphabetical.

R5 — Confidence score

Display threshold (per consumer):

ConfidenceBadge
≥ 90%high (no badge — implicit)
70–89%"medium" badge (text-muted)
< 70%"low" badge (warning); strong hint pra verify
missingomit confidence display

Confidence vem do RAG/search backend (services/ai/rag/ ou services/ai/search/). Não computado client-side.

R6 — Copy preserves citations

When user copies text from bubble:

  • Plain text mode: includes [1] [2] markers + appended footnotes:
    São Paulo has 12 million residents[1] and is the largest city in Brazil[2].
    
    [1] São Paulo - Population statistics — https://weather.com/sao-paulo
    [2] IBGE 2024 census — https://ibge.gov.br/cidades
    
  • Markdown mode: standard footnote syntax ([^1][^1]: ...).
  • Rich text mode: hyperlinks preserved.

Configurable per user preference (default markdown mode).

R7 — Surface bindings

SurfaceAPI
FlutterKoderCitation({required source}) + KoderCitationList({required sources}) em koder_kit/lib/src/ai/citation.dart
Web<koder-citation source-id="..."> + <koder-citation-list>
Compose AndroidKoderCitation em koder-design-compose (futuro)
SwiftUI iOSKoderCitation em koder-design-swift (futuro)
CLI / TUIInline: ^1 markers; sources block at end

R8 — Acessibilidade

  • Inline citation: <sup><a href="#source-1" aria-label="Source 1: weather.com">[1]</a></sup>.
  • Preview card: role="tooltip" quando aberto por hover; role="dialog" quando aberto por tap (focus management).
  • Sources list: <ol> (ordered) com semantically correct numbering.
  • Keyboard nav: Tab cycle entre citations; Enter opens source.
  • Screen reader: announces "Citation 1: weather.com, São Paulo Population, 92% confidence."

R9 — Multi-tenant + storage

  • Source URLs MUST NOT leak cross-tenant: workspace A RAG search hits never visible em workspace B.
  • Memory-sourced citations: cross-link memory-drawer.kmd (#117); memory items scoped per workspace.
  • Audit log: every citation render event optional (controlado via services/foundation/audit/ config).
  • Source URLs in retention: respeita policies/identity-data-retention.kmd.

R10 — i18n

Keyen-USpt-BR
ai.citation.sources_header"Sources""Fontes"
ai.citation.confidence"Confidence""Confiança"
ai.citation.confidence_low"Low confidence — verify carefully""Confiança baixa — verifique com cuidado"
ai.citation.confidence_medium"Medium confidence""Confiança média"
ai.citation.open_source"Open source""Abrir fonte"
ai.citation.fetched_relative"{relative_time}""{relative_time}"
ai.citation.no_sources"No sources cited""Nenhuma fonte citada"

R11 — Per-preset variation

PresetCitation style
material3 / material_expressiveDefault superscript, card with shadow
material2Superscript, card without shadow
terminal_classicInline ^[1] text; preview is plain text below
brutalistSquare brackets [1], sharp card border
minimalist_mono[1] mono, card minimal
cyberpunk_neonGlow superscript, neon outline preview
glassmorphismFrosted glass preview card

T-suite

  • T1 Inline render: response with 2 citations → [1] [2] superscripts visible at correct offsets.
  • T2 Hover preview (desktop): hover citation → card appears after 300ms.
  • T3 Long-press preview (mobile): long-press → card appears.
  • T4 Click navigates: tap inline → scrolls to source in sidebar/footer.
  • T5 Sources list render: footer mode shows ordered list with confidence.
  • T6 Confidence badge: source with 65% → "low" badge present.
  • T7 Copy preserves: select bubble text + copy → clipboard includes [1] [2] + footnotes.
  • T8 Sidebar sort: change sort to "recency" → list reorders.
  • T9 Keyboard nav: Tab through citations + Enter opens source.
  • T10 A11y screen reader: announce "Citation 1: ... 92% confidence."
  • T11 Multi-tenant: citation referencing source from workspace A NOT renderable in workspace B context.
  • N1 Missing citation in RAG-backed response: lint/policy violation (cross-link chat-message-bubble.kmd R2 disclaimer doesn't replace citation).
  • N2 Citation overflow offset (start > text length): graceful fallback (omit citation, log warning).
  • Companion: chat-message-bubble.kmd (host), memory-drawer.kmd (memory-sourced citations), ai-disclaimer.kmd (parallel transparency mechanism)
  • Backend: services/ai/rag/, services/ai/search/
  • Policies: multi-tenant-by-default.kmd, identity-data-retention.kmd, security.kmd
  • Color/typography: themes/color-roles.kmd, themes/typography.kmd
  • Compliance: EU AI Act art. 50 (transparency), LGPD art. 9

References