|
| 1 | +# ADR-004: Markdown som mellanformat |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +Accepterad |
| 6 | + |
| 7 | +## Kontext och problembeskrivning |
| 8 | + |
| 9 | +För att konvertera SFS-författningar från källdata (JSON) till olika output-format (HTML, Git, vektorembeddings) behövde vi välja en arkitektur för dataflödet. |
| 10 | + |
| 11 | +Huvudalternativen var: |
| 12 | + |
| 13 | +1. **Direkt konvertering**: JSON → HTML, JSON → Git, JSON → Vector (separata pipelines) |
| 14 | +2. **Mellanformat**: JSON → [Mellanformat] → (HTML/Git/Vector/etc) |
| 15 | + |
| 16 | +Utmaningarna var: |
| 17 | + |
| 18 | +- **Code duplication**: Varje exporter skulle behöva implementera samma parsing-logik |
| 19 | +- **Konsistens**: Hur garantera att HTML-versionen och Git-versionen är identiska? |
| 20 | +- **Mänsklig läsbarhet**: Utvecklare och bidragsgivare måste kunna granska output |
| 21 | +- **Versionskontroll**: Ska mellan-representationen kunna versionshanteras? |
| 22 | +- **Temporal processing**: Var i kedjan ska temporal filtrering appliceras? |
| 23 | +- **Underhållbarhet**: Ändringar i parsing-logik ska inte kräva uppdateringar i alla exporters |
| 24 | + |
| 25 | +## Beslut |
| 26 | + |
| 27 | +Vi använder **Markdown med HTML-taggar** som mellanformat mellan JSON-källdata och alla output-format. |
| 28 | + |
| 29 | +### Arkitektur |
| 30 | + |
| 31 | +``` |
| 32 | +JSON (Regeringskansliet) |
| 33 | + ↓ |
| 34 | +[format_sfs_text_as_markdown] |
| 35 | + ↓ |
| 36 | +Markdown med <section>-taggar och selex:-attribut |
| 37 | + ↓ |
| 38 | +├─→ [apply_temporal] → Markdown (rent) → HTML |
| 39 | +├─→ [apply_temporal] → Markdown (rent) → Git commits |
| 40 | +├─→ [apply_temporal] → Markdown (rent) → Vector embeddings |
| 41 | +└─→ Markdown (med taggar) → Publiceras som md-markers |
| 42 | +``` |
| 43 | + |
| 44 | +### Varför Markdown? |
| 45 | + |
| 46 | +**Mänsklig läsbarhet**: |
| 47 | +```markdown |
| 48 | +## 1 kap. Inledande bestämmelser |
| 49 | + |
| 50 | +### 1 § |
| 51 | +Denna lag gäller för... |
| 52 | +``` |
| 53 | + |
| 54 | +**Semantisk struktur** (behålls via HTML i Markdown): |
| 55 | +```html |
| 56 | +<section class="kapitel" selex:ikraft_datum="2025-01-01"> |
| 57 | +## 1 kap. Inledande bestämmelser |
| 58 | +... |
| 59 | +</section> |
| 60 | +``` |
| 61 | + |
| 62 | +**Versionskontroll**: |
| 63 | +- Markdown är text-baserat → perfekt för Git diff |
| 64 | +- Granskare kan läsa ändringar direkt på GitHub |
| 65 | +- Merge conflicts är läsbara och lösbara |
| 66 | + |
| 67 | +### Implementation |
| 68 | + |
| 69 | +**Steg 1: JSON → Markdown** (`formatters/format_sfs_text.py`) |
| 70 | +- Konverterar JSON-strukturen till Markdown-rubriker |
| 71 | +- Lägger till `<section>`-taggar med CSS-klasser |
| 72 | +- Extraherar och annoterar temporal metadata som `selex:`-attribut |
| 73 | + |
| 74 | +**Steg 2: Markdown → Output-format** |
| 75 | +- **HTML**: `markdown.markdown()` + custom extensions → HTML |
| 76 | +- **Git**: Temporal filtrering + commit med markdown-filer |
| 77 | +- **Vector**: Temporal filtrering + chunking + embedding |
| 78 | +- **md-markers**: Ingen transformation (publiceras som är) |
| 79 | + |
| 80 | +## Konsekvenser |
| 81 | + |
| 82 | +### Positiva |
| 83 | + |
| 84 | +- **Single source of truth**: All parsing-logik finns på ett ställe (`format_sfs_text.py`) |
| 85 | +- **Konsistens**: Alla output-format utgår från samma Markdown-representation |
| 86 | +- **Granskning**: Pull requests visar Markdown-diff som är läsbar för människor |
| 87 | +- **Flexibilitet**: Lätt att lägga till nya output-format |
| 88 | + - Vill du ha PDF? Konvertera Markdown → PDF med pandoc |
| 89 | + - Vill du ha EPUB? Markdown → EPUB med tooling |
| 90 | +- **Standard tooling**: Markdown har enormt ekosystem (parsers, linters, previewers) |
| 91 | +- **Hybrid format**: HTML-i-Markdown ger både läsbarhet och semantik |
| 92 | +- **Debug-vänligt**: Kan inspektera Markdown-filer manuellt vid problem |
| 93 | +- **Cacheable**: Markdown-filer kan sparas och återanvändas |
| 94 | +- **Testbarhet**: Enkelt att skriva tester mot Markdown-output |
| 95 | + |
| 96 | +### Negativa |
| 97 | + |
| 98 | +- **Extra konverteringssteg**: JSON → Markdown → HTML tar mer tid än direkt JSON → HTML |
| 99 | + - Mitigering: Prestandan är acceptabel, och flexibiliteten väger tyngre |
| 100 | + |
| 101 | +- **HTML-i-Markdown komplexitet**: Inte alla Markdown-renderare hanterar HTML perfekt |
| 102 | + - Mitigering: Vi använder beprövade bibliotek (`markdown` för Python) |
| 103 | + |
| 104 | +- **Temporal metadata i HTML-attribut**: Okonventionellt för Markdown |
| 105 | + - Mitigering: Dokumenterat i ADR-001, fungerar i praktiken |
| 106 | + |
| 107 | +- **Två Markdown-varianter**: `md-markers` (med taggar) vs `md` (rent) |
| 108 | + - Mitigering: Tydlig separation, olika användningsfall |
| 109 | + |
| 110 | +### Tekniska konsekvenser |
| 111 | + |
| 112 | +- **Format-specificering**: `--formats md-markers` vs `--formats md` |
| 113 | + - `md-markers`: Bevarar all metadata, för vidare processing |
| 114 | + - `md`: Rendad version efter temporal filtrering |
| 115 | + |
| 116 | +- **Temporal processing**: Sker EFTER Markdown-generering |
| 117 | + - Markdown → `apply_temporal(target_date)` → Rendad Markdown |
| 118 | + |
| 119 | +- **Lazy evaluation**: Temporal filtrering kan postponas till senare |
| 120 | + - `md-markers` sparar alla möjligheter öppna |
| 121 | + |
| 122 | +## Alternativ som övervägdes |
| 123 | + |
| 124 | +### 1. Direkt JSON → HTML/Git/Vector |
| 125 | + |
| 126 | +**Fördelar**: |
| 127 | +- Snabbare (färre steg) |
| 128 | +- Enklare arkitektur |
| 129 | + |
| 130 | +**Varför inte valt**: |
| 131 | +- Code duplication: Varje exporter måste parsa JSON |
| 132 | +- Konsistensproblem: Olika exporters kan tolka JSON olika |
| 133 | +- Ingen human-readable mellanrepresentation |
| 134 | +- Svårt att granska output (JSON är inte läsbart som Markdown) |
| 135 | + |
| 136 | +### 2. XML som mellanformat (TEI eller custom) |
| 137 | + |
| 138 | +```xml |
| 139 | +<law id="2024:100"> |
| 140 | + <chapter number="1"> |
| 141 | + <heading>Inledande bestämmelser</heading> |
| 142 | + <paragraph number="1"> |
| 143 | + <text>Denna lag gäller...</text> |
| 144 | + </paragraph> |
| 145 | + </chapter> |
| 146 | +</law> |
| 147 | +``` |
| 148 | + |
| 149 | +**Fördelar**: |
| 150 | +- Strikt struktur med validering |
| 151 | +- Etablerad standard (TEI) inom juridisk text |
| 152 | + |
| 153 | +**Varför inte valt**: |
| 154 | +- Mindre läsbart för människor |
| 155 | +- Högre inlärningströskel |
| 156 | +- Mindre ekosystem än Markdown |
| 157 | +- Svårare att granska i pull requests |
| 158 | +- Overkill för vårt användningsfall |
| 159 | + |
| 160 | +### 3. JSON som mellanformat (normaliserad struktur) |
| 161 | + |
| 162 | +**Varför inte valt**: |
| 163 | +- Inte mänskligt läsbart |
| 164 | +- Svårt att versionskontrollera (JSON diff är svårläst) |
| 165 | +- Inget naturligt sätt att representera flytande text med rubriker |
| 166 | + |
| 167 | +### 4. AST (Abstract Syntax Tree) |
| 168 | + |
| 169 | +```python |
| 170 | +{ |
| 171 | + "type": "document", |
| 172 | + "children": [ |
| 173 | + {"type": "chapter", "number": "1", "heading": "..."}, |
| 174 | + {"type": "paragraph", "number": "1", "content": "..."} |
| 175 | + ] |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +**Varför inte valt**: |
| 180 | +- Rent programmatiskt format, inte läsbart |
| 181 | +- Måste serialiseras för att inspekteras |
| 182 | +- Ingen standard representation |
| 183 | +- Kan inte versionskontrolleras meningsfullt |
| 184 | + |
| 185 | +## Relaterade beslut |
| 186 | + |
| 187 | +- [ADR-001](001-semantiska-temporal-taggar.md) - Selex-attribut i Markdown |
| 188 | +- [ADR-003](003-git-commits-historiska-datum.md) - Markdown-filer committas till Git |
| 189 | + |
| 190 | +## Noteringar |
| 191 | + |
| 192 | +- **Implementationer**: |
| 193 | + - `formatters/format_sfs_text.py` - JSON → Markdown konvertering |
| 194 | + - `formatters/frontmatter_manager.py` - YAML frontmatter i Markdown |
| 195 | + - `temporal/apply_temporal.py` - Temporal filtrering av Markdown |
| 196 | + - `exporters/html/html_export.py` - Markdown → HTML med `markdown.markdown()` |
| 197 | + - `exporters/vector/chunking.py` - Markdown → Chunks för embeddings |
| 198 | + |
| 199 | +- **Markdown flavor**: CommonMark-kompatibel med HTML-extension |
| 200 | + - Rubriker: Standard Markdown (`##`, `###`, `####`) |
| 201 | + - Paragrafer: Standard Markdown (dubbla radbrytningar) |
| 202 | + - Semantik: HTML `<section>` och `<article>` taggar |
| 203 | + - Links: Standard Markdown-länkar + auto-linking |
| 204 | + |
| 205 | +- **Frontmatter**: YAML frontmatter för metadata |
| 206 | + ```yaml |
| 207 | + --- |
| 208 | + beteckning: "2024:100" |
| 209 | + rubrik: "Lag om exempel" |
| 210 | + ikraft_datum: "2025-01-01" |
| 211 | + --- |
| 212 | + ``` |
| 213 | + |
| 214 | +- **Fördelar med två varianter**: |
| 215 | + - **md-markers**: För vidare processing, AI-analys, temporal queries |
| 216 | + - **md**: För publicering, läsning, GitHub Pages |
| 217 | + |
| 218 | +- **Future-proof**: Om nya output-format behövs (PDF, DOCX, LaTeX): |
| 219 | + - Använd befintlig Markdown → Konvertera med standard tooling |
| 220 | + - Ingen ändring i core parsing-logik behövs |
| 221 | + |
| 222 | +- **Exempel på Markdown-ekosystem som vi drar nytta av**: |
| 223 | + - `python-markdown`: Markdown → HTML konvertering |
| 224 | + - GitHub/GitLab: Automatisk rendering av `.md` filer |
| 225 | + - Markdown linters: Kvalitetskontroll av output |
| 226 | + - Markdown previewers: Live-förhandsvisning under utveckling |
| 227 | + - Pandoc: Potentiell framtida konvertering till andra format |
0 commit comments