server/docs/features/chat.md
vegard a5985ef3f8 Dokumentasjon, erfaringslogg, migrasjoner og infra-oppdateringer
- Omorganiser docs/: konsepter, features, infra og proposals i egne mapper
- Ny docs/erfaringer/ med lærdommer fra chat-implementering (Svelte 5, SpacetimeDB, adapter-mønster)
- Oppdater ARCHITECTURE.md: Lag 1 status, ny §10 Erfaringslogg, SpacetimeDB i lokal dev
- Oppdater synkronisering.md med implementeringsstatus og designvalg
- Oppdater lokal.md med SpacetimeDB og AI Gateway
- Utvid PG-skjema med channels, messages, media_files, message_revisions
- Legg til seed_dev.sql, migration_safety.md, .env.example
- Nye feature-specs: chat, kanban, whiteboard, live_ai, lydmeldinger m.fl.
- Nye konsept-specs: studioet, møterommet, redaksjonen, den asynkrone gjesten m.fl.
- SpacetimeDB og AI Gateway i docker-compose.dev.yml
- collect-docs.sh inkluderer erfaringer/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:40:14 +01:00

8.3 KiB

Feature: Chat (Channels & Meldinger)

Filsti: docs/features/chat.md

1. Konsept

En universell, sanntids meldingskomponent bygget på SpacetimeDB. Chat er ikke bundet til én kontekst — den kan knyttes til enhver node i Kunnskapsgrafen via channels. Ulike konsepter bruker chat med ulik konfigurasjon, men all infrastruktur er delt.

2. Channels-modellen

En channel er en meldingsstrøm knyttet til en vilkårlig node (parent) i grafen. Channelen er selv en node (node_type = 'channel'), noe som betyr at den deltar i grafen og arver workspace-isolasjon.

┌─────────────┐     parent_id      ┌─────────────┐
│   Channel   │ ──────────────────► │ Vilkårlig   │
│   (node)    │                     │ node        │
└──────┬──────┘                     └─────────────┘
       │
       │  channel_id
       ▼
┌─────────────┐
│  Meldinger  │
│  (nodes)    │
└─────────────┘

En node kan ha flere channels. Eksempler:

  • Et Tema har "Diskusjon" (default) + "Research-dump"
  • En Episode har "Redaksjonelt" (intern kommentartråd)
  • Et Møte har "Scratchpad" (flyktig, TTL)
  • En Aktør har "Notater" (valgfri)

2.1 Channel-konfigurasjon

Hver channel har en config (JSONB) som styrer hvilke capabilities den støtter:

{
  "threads": true,          // reply-to-tråder
  "mentions": true,         // #/@ parsing + automatiske graph_edges
  "attachments": true,      // filopplasting
  "research_clips": true,   // research_clip meldingstype (AI-prosessert)
  "ttl_days": null           // null = permanent, tall = auto-slett etter N dager
}

2.2 Standard channel-presets per konsept

Konsept Channel-navn Config
Redaksjonen (Tema) "Diskusjon" threads: true, mentions: true, attachments: true, research_clips: true, ttl_days: null
Redaksjonen (Tema) "Research" (valgfri) threads: false, mentions: true, attachments: true, research_clips: true, ttl_days: null
Møterommet "Scratchpad" threads: false, mentions: false, attachments: false, research_clips: false, ttl_days: 90
Studioet "Studio-chat" threads: false, mentions: false, attachments: false, research_clips: false, ttl_days: 30
Episode "Redaksjonelt" threads: true, mentions: true, attachments: true, research_clips: false, ttl_days: null

Presets er kun defaults — en workspace-admin kan justere config per channel.

2.3 Automatisk opprettelse

Når en node opprettes som forventes å ha chat (Tema, Episode, Møte), oppretter systemet automatisk en default-channel. Dette skjer i SvelteKit server-side som en del av node-opprettelsestransaksjonen.

3. Meldinger

3.1 Datamodell (PostgreSQL)

Meldinger er noder i Kunnskapsgrafen (node_type = 'melding'):

messages (
    id           UUID PK  nodes(id),
    channel_id   UUID NOT NULL  channels(id),
    reply_to     UUID  messages(id),       -- tråder (hvis config.threads = true)
    author_id    TEXT NOT NULL  users,
    message_type message_type,              -- 'text', 'research_clip', 'factoid', 'system'
    body         TEXT NOT NULL,
    metadata     JSONB,                     -- ekstra data (research-klipp AI-resultat, etc.)
    edited_at    TIMESTAMPTZ,
    created_at   TIMESTAMPTZ
)

3.2 SpacetimeDB (sanntid)

SpacetimeDB holder aktive channels' meldinger i minnet. Nye meldinger sendes via SpacetimeDB og kringkastes til alle tilkoblede klienter i samme workspace og channel. Synkes til PostgreSQL med ~5 sek forsinkelse (se docs/infra/synkronisering.md).

4. Mentions & Autocomplete

Kun aktive når config.mentions = true.

  • Trigger-tegn: # (Temaer/Aktører fra Kunnskapsgrafen), @ (brukere/redaksjonsmedlemmer), / (kommandoer).
  • Filtrering: Svelte-klienten filtrerer den lokale SpacetimeDB-cachen umiddelbart. Skriver man #Ha... vises en klikkbar liste ("Hans Petter Sjøli", "Høyre").
  • Grafkobling: Ved #-mention opprettes automatisk MENTIONS-edges i graph_edges mellom meldingen og den nevnte noden.
  • Mobil-optimalisert: Autocomplete-listen er tappbar og tilpasset mindre skjermer.

5. Tråder

Kun aktive når config.threads = true. Meldinger kan ha en reply_to-referanse. Frontend viser tråder som innrykk eller ekspanderbare grupper.

6. Vedlegg

Kun aktive når config.attachments = true. Meldinger kan ha vedlegg via message_attachmentsmedia_files. Whiteboard-eksport kan knyttes som vedlegg.

7. Versjonshistorikk

Alle meldinger støtter redigering med full historikk via message_revisions. Original tekst bevares alltid.

8. Tale-til-tekst (Voice-to-text)

Mobilvennlig diktering for situasjoner der tastatur er upraktisk. Brukeren trykker en mikrofon-knapp, snakker, og får teksten tilbake som en vanlig melding klar til redigering og sending.

8.1 Flyt

  1. Bruker trykker og holder (eller toggler) mikrofon-knappen i chat-feltet.
  2. Nettleseren fanger lyd via MediaRecorder API (WebM/Opus).
  3. Lydklippet sendes til Whisper (POST /v1/audio/transcriptions, response_format=text, language=no) via SvelteKit server-side.
  4. Transkripsjonen settes inn i meldingsfeltet — brukeren kan redigere før sending.
  5. Ingen lagring av lydfilen — den kastes etter transkripsjon.

8.2 Avgrensning

  • Dette er ikke en lydmelding-feature (à la WhatsApp). Lyden er et transportmiddel for tekst. Kun teksten lagres.
  • Whisper-kallet er kort (<30 sek tale) og kan rutes direkte til Whisper-serveren uten jobbkø.
  • Bruk small-modellen for lav latens. Navnenøyaktighet er mindre viktig for korte chatmeldinger.

9. TTL (automatisk opprydding)

Channels med config.ttl_days satt til et tall får sine meldinger automatisk slettet av en nattlig jobbkø-jobb. Brukes for flyktige kontekster (scratchpads, studio-chat).

10. Implementeringsstatus

Ferdig (mars 2025)

  • ChatBlock.svelte: Refaktorert til adapter-mønster via createChat() factory
  • PG-adapter (pg.svelte.ts): Polling hvert 3. sek, full-fetch (ingen akkumulering). Fungerer som selvstendig fallback.
  • SpacetimeDB hybrid-adapter (spacetime.svelte.ts): Henter historikk fra PG via REST + lytter på SpacetimeDB WebSocket for sanntidspush. Dedupliserer mot PG-data. Faller tilbake til PG REST ved sending hvis SpacetimeDB er nede.
  • Factory (create.svelte.ts): Velger adapter basert på VITE_SPACETIMEDB_URL. SSR-safe med browser-guard.
  • Shared types (types.ts): Message og ChatConnection interface brukt av begge adaptere.
  • SpacetimeDB Rust-modul (spacetimedb/src/lib.rs): ChatMessage-tabell, SyncOutbox, send_message-reducer. Publisert som sidelinja-realtime.
  • TypeScript-bindings: Generert fra SpacetimeDB-modulen, camelCase-properties.
  • Autoscroll: $effect som scroller ned ved nye meldinger.
  • Datogruppering: Visuell datoseparator ("I dag", "I går", dato).

Gjenstår

  • Sync-worker (SpacetimeDB → PG): Rust-worker som poller sync_outbox og persisterer til PostgreSQL. Se docs/infra/synkronisering.md §5.1.
  • Tråder, mentions, vedlegg, TTL — avventer Kunnskapsgraf CRUD (Lag 2).
  • Autentisering: author_name/author_id settes ikke automatisk ennå (avventer Authentik-integrasjon).

11. Instruks for Claude Code

  • Opprettelsesrekkefølge: Opprett nodes-rad → channels-rad → (for meldinger) nodes-rad → messages-rad. Alt i én transaksjon med riktig workspace_id.
  • Channel-opprettelse: Når en Tema, Episode eller Møte opprettes, opprett alltid en default-channel i samme transaksjon.
  • Mentions-parsing: Skjer i SvelteKit server-side ved mottak av meldingen. Parse #- og @-tags, valider mot noder i workspace-et, og opprett graph_edges.
  • Config-respekt: Frontend-komponenten må lese channel.config og slå av/på UI-elementer (tråd-knapp, vedlegg-knapp, mention-autocomplete) basert på config.
  • SpacetimeDB er autoritativ for aktive meldinger, PG for historikk.
  • Alt er workspace-scopet. Channels arver workspace via nodes.workspace_id.