AI citations / source attribution

mandatory

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.

Spec — AI citations / source attribution

RAG/search-backed AI responses *UST*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. *nline + sidebar dual*— cada claim com superscript [N]; sources list completa em sidebar/footer.
  2. *lick reveals source*— não exige scroll: tap inline → preview card.
  3. *onfidence transparente*— quando RAG fornece relevance score, expose.
  4. *opy preserves citations*— copytoclipboard mantém footnote refs.
  5. *orkspace-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: scrolltosource 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 chatmessagebubble (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):

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

Confidence vem do RAGsearch backend (`servicesairag ou servicesaisearch/`). 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:/eather.com/sao-paulo [2] IBGE 2024 census — https:/bge.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

Surface API
Flutter KoderCitation({required source}) + KoderCitationList({required sources}) em koder_kit/lib/src/ai/citation.dart
Web <koder-citation source-id="..."> + <koder-citation-list>
Compose Android KoderCitation em koder-design-compose (futuro)
SwiftUI iOS KoderCitation em koder-design-swift (futuro)
CLI / TUI Inline: ^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.
  • Memorysourced citations: crosslink 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

Key en-US pt-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

Preset Citation style
material3 / material_expressive Default superscript, card with shadow
material2 Superscript, card without shadow
terminal_classic Inline ^[1] text; preview is plain text below
brutalist Square brackets [1], sharp card border
minimalist_mono [1] mono, card minimal
cyberpunk_neon Glow superscript, neon outline preview
glassmorphism Frosted glass preview card

T-suite

  • *1*Inline render: response with 2 citations → [1] [2] superscripts visible at correct offsets.
  • *2*Hover preview (desktop): hover citation → card appears after 300ms.
  • *3*Longpress preview (mobile): longpress → card appears.
  • *4*Click navigates: tap inline → scrolls to source in sidebar/footer.
  • *5*Sources list render: footer mode shows ordered list with confidence.
  • *6*Confidence badge: source with 65% → "low" badge present.
  • *7*Copy preserves: select bubble text + copy → clipboard includes [1] [2] + footnotes.
  • *8*Sidebar sort: change sort to "recency" → list reorders.
  • *9*Keyboard nav: Tab through citations + Enter opens source.
  • *10*A11y screen reader: announce "Citation 1: ... 92% confidence."
  • *11*Multi-tenant: citation referencing source from workspace A NOT renderable in workspace B context.
  • *1*Missing citation in RAGbacked response: lint/policy violation (crosslink chat-message-bubble.kmd R2 disclaimer doesn't replace citation).
  • *2*Citation overflow offset (start > text length): graceful fallback (omit citation, log warning).
  • Companion: chat-message-bubble.kmd (host), memory-drawer.kmd (memorysourced citations), [`aidisclaimer.kmd`](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
  • Colortypography: `themescolor-roles.kmd, themes/typography.kmd`
  • Compliance: EU AI Act art. 50 (transparency), LGPD art. 9

Source: ../home/koder/dev/koder/meta/docs/stack/specs/ai-ui/citations.kmd