Danish statute + EUR-Lex RAG pipeline with clickable inline citations
marklok adds a full legal-retrieval stack over four commits: pgvector-backed ingestion for Retsinformation statutes and EUR-Lex regulations, section-reference-aware retrieval, and a slide-in citation panel in the chat UI. The fork is clearly targeting Danish legal practice.
The retrieval logic in lawRetrieval.ts goes beyond vanilla embedding search. It detects structured references like "§ 42, stk. 3" or "artikel 5 DORA" via regex (backed by a LAW_ALIASES map), runs a metadata lookup for that exact section in parallel with the cosine similarity search, then merges and deduplicates the results. That hybrid pattern - metadata fallback alongside embedding search - is the most reusable piece here, applicable to any domain with structured citation syntax.
Ingestion targets Danish primary-source statutes (hvidvaskloven, funktionærloven, selskabsloven) and EU regulations (DORA 32022R2554, PRIIPs 32014R1286), driven by env vars (RETSINFORMATION_LAWS, EURLEX_REGULATIONS). The backend migration adds a document_chunks table with an IVFFlat cosine index. There's a minor inconsistency: 001_document_chunks.sql creates the index with lists=100, then 002_reindex_chunks.sql immediately rebuilds it at lists=20 to reduce memory pressure.
Two other fixes come bundled in this commit set and are worth pulling on their own. normalizeCitation was silently dropping citations with page:null - the fix treats null as page=0. And the stop button was showing while the invisible <CITATIONS> SSE block was still streaming; showStopButton = isLoading && !isLoadingCitations fixes that so users can't accidentally cancel before citations arrive.
The law context is appended unconditionally to every chat turn, which will eat tokens on non-legal queries. The ingestion scripts call live endpoints with no visible rate-limiting.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?