# Traits — Evner og funksjonalitet for noder

**Status:** Vedtatt

## Konsept

En **trait** er en navngitt evne som beriker en samlingsnode med spesifikk
funksjonalitet. Traits bestemmer hva en samling *kan gjøre* — hvilke
UI-komponenter som vises i frontend og hvilken backend-oppførsel maskinrommet
aktiverer.

Traits er mekanismen som gjør at én og samme node-/edge-arkitektur kan
fungere som nettmagasin, podcaststudio, diskusjonsklubb, wiki, eller en
kombinasjon av flere.

## Designprinsipper

1. **Komposisjon, ikke typer.** En samling er ikke "et magasin" — den har
   traits som *gjør den til* et magasin. Legg til eller fjern traits når
   som helst uten migrasjon.

2. **Traits i metadata, ikke edges.** Traits er egenskaper ved samlingen,
   ikke relasjoner mellom entiteter. Edges er for relasjoner (innhold →
   samling). Metadata er for konfigurasjon (hva samlingen kan gjøre).

3. **Lukket katalog, åpen konfigurasjon.** Trait-navn er et kjent sett vi
   eier koden for. Konfigurasjonen innenfor hver trait er fleksibel JSONB.

4. **Uavhengige traits.** Hver trait fungerer alene. Verdien oppstår i
   sammensetningen — og det er brukeren som setter sammen, ikke utvikleren.

5. **To effekter per trait.** Hver trait aktiverer:
   - **Frontend:** UI-komponenter, visninger, interaksjoner
   - **Backend:** Maskinrommet-oppførsel, validering, jobb-triggering

## Metadata-struktur

Traits lever i samlingsnodenes `metadata.traits`-objekt:

```jsonc
{
  "node_kind": "collection",
  "title": "Sidelinja Magasin",
  "metadata": {
    "traits": {
      "publishing": {
        "slug": "sidelinja",
        "custom_domain": "magasin.sidelinja.org",
        "theme": "editorial",
        "open_graph_defaults": { "image": "cas://sha256-abc123" }
      },
      "editor": {
        "preset": "longform",
        "allow_collaborators": true
      },
      "rss": {
        "format": "atom",
        "title": "Sidelinja Magasin",
        "max_items": 50
      },
      "comments": {
        "moderation": "pre-approve",
        "anonymous": false
      }
    }
  }
}
```

Fravær av en trait betyr at funksjonaliteten er deaktivert. Ingen boolean
`enabled`-flagg — traiten finnes eller finnes ikke.

## Trait-katalog

### Innhold & redigering

| Trait | Frontend | Backend |
|---|---|---|
| `editor` | TipTap med presets (longform, note, chat, code) | Validering av dokumentstruktur |
| `versioning` | Revisjonshistorikk, diff, rollback-knapp | Snapshot ved signifikante endringer |
| `collaboration` | Samtidig redigering, markører, inline-kommentarer | OT/CRDT via WebSocket |
| `translation` | Språkvelger, side-ved-side-visning | AI-oversettelse via jobbkø |
| `templates` | Mal-velger ved ny node | Mal-noder i samlingen |

### Publisering & distribusjon

| Trait | Frontend | Backend |
|---|---|---|
| `publishing` | Publiseringsknapp, forhåndsvisning, URL-visning, SEO-editor | HTML-rendering til CAS, Caddy-ruting, OG-tags |
| `rss` | Feed-URL synlig i UI | Feed-generering ved publisering |
| `newsletter` | Abonnentliste, utsendingsknapp, forhåndsvisning | E-postutsending ved publisering |
| `custom_domain` | Domene-innstilling i admin | Caddy on-demand TLS, DNS-validering |
| `analytics` | Besøksstatistikk-dashbord | Logg-parsing, IAB-filtrering |
| `embed` | Kopier embed-kode-knapp | Generer iframe-snippet med riktige dimensjoner |
| `api` | API-dokumentasjon, nøkkelhåndtering | Offentlig JSON-endepunkt for samlingens innhold |

### Lyd & video

| Trait | Frontend | Backend |
|---|---|---|
| `podcast` | Episodeliste, lydavspiller, RSS-lenke | RSS med enclosures, metadata-håndtering |
| `recording` | LiveKit-studio, opptak-kontroller | LiveKit-rom, lydkonsolidering |
| `transcription` | Transkripsjonsvisning, redigerbar SRT | Whisper-pipeline via jobbkø |
| `tts` | "Les opp"-knapp, lydversjon av artikler | Tekst-til-tale via jobbkø |
| `clips` | Klipp-editor, segment-markering | Segmentering, CAS-lagring av klipp |
| `playlist` | Ordnet avspillingsliste, drag-and-drop rekkefølge | Sekvensiell avspilling |
| `mixer` | Lydmixer: volumslidere, mute, VU-meter, sound pads, stemme-/EQ-effekter | Pad-konfig i metadata (se `docs/features/lydmixer.md`) |
| `studio` | Lydstudio-panel: lydfilliste med metadata, drag-ut, drop-aksept for lyd, lenke til fullverdig editor | Validering av lydformat, FFmpeg-pipeline via jobbkø (se `docs/features/lydstudio.md`) |

### Kommunikasjon

| Trait | Frontend | Backend |
|---|---|---|
| `chat` | Sanntidsmeldinger, tråder, reaksjoner | WebSocket-synk, TTL-håndtering |
| `forum` | Trådet diskusjon, sortering (nyeste/populære/ubesvarte) | Tråd-indeksering |
| `comments` | Kommentarfelt under publisert innhold | Moderasjonskø, evt. anonym input |
| `guest_input` | Gjeste-lenke-generering, svar-oversikt | Token-generering, CAS-upload, rate limiting |
| `announcements` | Enveis-feed, kun eiere/admins poster | Skrivetilgangsvalidering |
| `polls` | Avstemnings-UI, resultatvisning | Stemmetelling, duplikatdeteksjon |
| `qa` | Spørsmål/svar-format, oppstemming, "godkjent svar" | Sortering, markering |

### Organisering

| Trait | Frontend | Backend |
|---|---|---|
| `kanban` | Board med kolonner, drag-and-drop | Statusoverganger, posisjonsberegning |
| `calendar` | Kalendervisning (måned/uke/dag) | Scheduling-edges, ICS-eksport |
| `timeline` | Kronologisk visning | Tidsbasert sortering og filtrering |
| `table` | Strukturert tabellvisning, filtrerbare kolonner | Metadata-indeksering |
| `gallery` | Rutenett av bilde-/medianoder | Thumbnail-generering |
| `bookmarks` | Kuratert lenkesamling med forhåndsvisning | URL-enrichment via maskinrommet |
| `tags` | Tagging, filtrering, tag-sky | Tag-indeksering |

### Kunnskap

| Trait | Frontend | Backend |
|---|---|---|
| `knowledge_graph` | Visuell graf, auto-tagging | NER, embedding-generering |
| `wiki` | Slug-baserte sider, kryssreferanser, "finnes ikke ennå"-lenker | Slug-unikhet, backlink-indeks |
| `glossary` | Begrepsliste, hover-definisjoner i annet innhold | Begrep-matching i tekst |
| `faq` | Spørsmål/svar-par med søk | Søkeindeksering |
| `bibliography` | Kildehenvisninger, siteringsformat, referanseliste | Sitats-parsing, DOI-oppslag |

### Automatisering & AI

| Trait | Frontend | Backend |
|---|---|---|
| `auto_tag` | Foreslåtte tags ved ny node, godkjenn/avvis | AI-tagging via jobbkø |
| `auto_summarize` | AI-sammendrag synlig, redigerbart | Sammendrag-generering ved publisering/møteslutt |
| `digest` | Periodisk oppsummering i UI | AI-sammendrag av aktivitet på intervall |
| `bridge` | "Også funnet i..."-forslag | pgvector-embedding, krysskontekst-søk |
| `moderation` | Moderasjonskø, flagging | AI-assistert innholdsvurdering |
| `ai_tool` | AI-verktøy-panel med prompt-velger, drag-and-drop tekstbehandling | Modellprofil-mapping, AI Gateway, ai_usage_log (se `docs/features/ai_verktoy.md`) |

### Tilgang & fellesskap

| Trait | Frontend | Backend |
|---|---|---|
| `membership` | Søknads-/innmeldingsknapp | Søknadskø, godkjenningsflyt |
| `roles` | Rolletildeling i admin | Egendefinerte roller utover owner/admin/member/reader |
| `invites` | Invitasjonslenker med kopiering | Token-generering, utløp, maks bruk |
| `paywall` | Betalingsflyt, tilgangskontroll | Stripe/Vipps-integrasjon |
| `directory` | Medlemsoversikt med profiler | Profilindeksering |

### Ekstern integrasjon

| Trait | Frontend | Backend |
|---|---|---|
| `webhook` | Webhook-konfigurasjon i admin | HTTP-varsling ved hendelser |
| `import` | Import-veiviser (WordPress, Markdown, RSS) | Parsing, node-opprettelse, mediaimport |
| `export` | Eksport-knapp (Markdown, EPUB, PDF) | Format-konvertering, CAS-pakking |
| `ical_sync` | Kalender-URL, synk-status | Toveis ICS-synk |

## Pakker

Pakker er forhåndsdefinerte kombinasjoner av traits med fornuftige defaults.
En pakke er *ikke* en type eller en låst konfigurasjon — den er et startpunkt.
Brukeren kan legge til eller fjerne traits etterpå.

| Pakke | Traits |
|---|---|
| **Nettmagasin** | editor(longform), publishing, rss, comments, analytics, custom_domain, newsletter |
| **Podcaststudio** | podcast, recording, transcription, mixer, editor(shownotes), rss, analytics, clips, knowledge_graph |
| **Nyhetsbrev** | editor(longform), newsletter, analytics, versioning |
| **Wiki** | wiki, editor(longform), collaboration, versioning, knowledge_graph, glossary |
| **Diskusjonsklubb** | forum, chat, polls, membership, roles, directory |
| **Kursplattform** | editor(longform), playlist, qa, membership, paywall, templates |
| **Møteplass** | recording, mixer, chat, kanban, calendar, auto_summarize, guest_input |
| **Fotoblogg** | gallery, publishing, comments, custom_domain, rss |
| **Prosjektstyring** | kanban, calendar, chat, table, tags, roles |
| **Åpen forskning** | editor(longform), versioning, bibliography, publishing, comments, collaboration, api |
| **Community radio** | recording, mixer, podcast, chat, polls, membership, clips, playlist |
| **Bokmerke-vegg** | bookmarks, tags, publishing, rss, comments |
| **Redaksjon** | chat, kanban, calendar, editor(longform), knowledge_graph, guest_input |

## Implementeringsstrategi

Traits implementeres én og én, ikke alle samtidig. Prioritering følger
brukerbehovene til Sidelinja som første tenant. En trait krever:

1. **Spesifikasjon** — hva traiten gjør, metadata-skjema, UI-mockup
2. **Backend** — maskinrommet-kode som reagerer på traitens tilstedeværelse
3. **Frontend** — Svelte-komponent(er) som rendres når traiten er aktiv
4. **Dokumentasjon** — oppdater denne filen og evt. `docs/features/`

Rekkefølge for første bolk (Sidelinja-behov):
`editor` → `chat` → `publishing` → `rss` → `podcast` → `recording` →
`transcription` → `knowledge_graph` → `kanban` → `calendar`
