Nystart basert på arkitektonisk innsikt fra Sidelinja v1. Koden er ny, visjon og primitiver er validert gjennom tidligere arbeid. Inneholder: - Komplett arkitekturdokumentasjon (docs/arkitektur.md) - 6 vedtatte retninger (docs/retninger/) - Alle concepts, features, proposals og erfaringer fra v1 - Server-oppsett og drift (docs/setup/) - LiteLLM-konfigurasjon (API-nøkler via env) - Editor.svelte referanse fra v1 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
299 lines
14 KiB
Markdown
299 lines
14 KiB
Markdown
# Universell input og mottak
|
|
|
|
> Én multimodal input-primitiv. Én personlig mottaksflate. Alt som fanges
|
|
> er samme type objekt. Hva det "er" bestemmes av edges, ikke av tabellen
|
|
> det ligger i. Hvordan det *presenteres* bestemmes av mottakeren.
|
|
|
|
## Observasjoner
|
|
|
|
I dag har vi meldingsboksen som "universell primitiv" — men den er egentlig en
|
|
*lagringsprimitiv*. Den samler chat, kanban-kort, kalenderoppføringer og notater
|
|
i én tabell, men *input* er fortsatt forskjellig per kontekst: chat har ett
|
|
tekstfelt, kanban har et skjema, kalender har en datovelger. Brukeren velger
|
|
kontekst først, deretter gir de input.
|
|
|
|
Og vi har separate pipelines for ulike modaliteter: tekst går én vei, lyd
|
|
(voice/transkripsjon) en annen, bilder en tredje. Hver med sin egen flyt.
|
|
|
|
## Tesen
|
|
|
|
**Én input-primitiv, to versjoner:**
|
|
|
|
- **Sanntidsversjon** — live i SpacetimeDB-laget. Brukes i rom, samarbeid,
|
|
samtaler. Streamer input og viser resultater i sanntid.
|
|
- **Flat versjon** — tradisjonelt mot PG. Brukes på bussen, alene, offline-aktig.
|
|
Fanger input og lagrer asynkront.
|
|
|
|
Begge aksepterer alt:
|
|
- **Tekst** — skriving, Markdown, kodeblokker
|
|
- **Lyd** — voice memo, diktering → automatisk transkribert
|
|
- **Bilde** — foto, skjermbilde, tegning
|
|
- **AI-støtte** — spør AI, få forslag, la den transformere input
|
|
- **Nettoppslag** — lim inn URL, den berikes automatisk
|
|
- **Kommunikasjon** — samme primitiv for alene (dagbok), en-til-en (melding),
|
|
gruppe (kanal)
|
|
|
|
Forskjellen mellom "jeg skriver dagbok", "jeg sender en melding" og "jeg lager
|
|
et kanban-kort" er ikke *hva brukeren gjør* — det er hvilke edges som knyttes
|
|
til resultatet.
|
|
|
|
## Én tabell, edges definerer alt
|
|
|
|
All output fra input-primitiven lander som noder i kunnskapsgrafen. Én tabell.
|
|
Ingen `messages`-tabell, ingen `cards`-tabell, ingen `notes`-tabell.
|
|
|
|
Hva en node "er" bestemmes utelukkende av edges:
|
|
- Node + edge til kanal = chatmelding
|
|
- Node + edge til board + status-edge = kanban-kort
|
|
- Node + edge til dato = kalenderoppføring
|
|
- Node + edge til kun bruker (privat) = dagboknotis
|
|
- Node + edge til topic = faktoid i kunnskapsgrafen
|
|
- Node uten edges = løs tanke, ennå uorganisert
|
|
|
|
**Retyping er trivielt.** Å gjøre en chatmelding om til et kanban-kort er å
|
|
legge til en edge til et board og en status-edge. Fjerne fra chat er å fjerne
|
|
kanal-edgen. Ingen datamigrering, ingen transformasjon av innhold. Bare edges.
|
|
|
|
**Multitype er naturlig.** En node kan være *både* et kanban-kort *og* en
|
|
kalenderoppføring *og* en faktoid. Det er ikke en edge case — det er
|
|
arkitekturen.
|
|
|
|
## Implikasjoner
|
|
|
|
### Meldingsboksen erstattes av noe dypere
|
|
Meldingsboksen var riktig intuisjon — men den er en lagringsprimitiv som
|
|
prøver å forene ulike domenemodeller. Universell input + kunnskapsgrafen
|
|
gjør det renere: det finnes bare noder og edges. "Meldingsboks" blir et
|
|
view-konsept (hvordan noder vises i en kontekst), ikke et lagrings-konsept.
|
|
|
|
### Input-metode og innholdstype er ortogonale
|
|
Du kan snakke inn et kanban-kort. Du kan tegne en kalenderoppføring. Du kan
|
|
skrive en voice memo (tekst som transkriberes til lyd for en annen bruker).
|
|
Input-primitiven bryr seg ikke om hva det *blir* — den fanger det som
|
|
kommer inn.
|
|
|
|
### Samme input, ulik routing
|
|
Lyd inn i input-primitiven kan routes helt forskjellig basert på edges:
|
|
- Edge til et møterom → streames live til andre deltakere (sanntidslaget)
|
|
- Edge til kun deg selv → transkriberes og lagres som personlig notat
|
|
- Edge til en podcast-kanal → goes into produksjonspipeline
|
|
- Edge til en person → sendes som lydmelding
|
|
|
|
Brukeren gjør det samme — snakker inn i input-feltet. *Systemet* router
|
|
basert på kontekst og edges. Det er ingen "møte-app" eller "notat-app"
|
|
eller "meldings-app" — det er én input med ulike destinasjoner.
|
|
|
|
### Mottaker bestemmer format
|
|
All lyd transkriberes. All tekst kan leses opp (TTS). Noden har alltid
|
|
begge representasjoner. Mottaker setter sin preferanse:
|
|
- Trond snakker inn en tanke → node med lyd + transkripsjon
|
|
- Peter har tekst-preferanse → ser transkripsjonen
|
|
- Vegard har lyd-preferanse → hører originallyd
|
|
- Anna skriver tekst → node med tekst + TTS-versjon
|
|
- Trond har lyd-preferanse → hører TTS-opplesning av Annas tekst
|
|
|
|
Senderen trenger ikke vite eller bry seg. Innholdet er det samme —
|
|
presentasjonen er en mottaker-side preferanse. Modalitet er ikke
|
|
en egenskap ved meldingen, men ved *lesningen* av den.
|
|
|
|
### Én overflate å perfeksjonere
|
|
Brukerens mentale modell kollapser til én ting: input-feltet. All
|
|
UX-investering konsentreres ett sted i stedet for å smøres tynt utover
|
|
ti ulike grensesnitt. Én perfekt input-opplevelse — responsiv, multimodal,
|
|
med god AI-støtte — i stedet for ti middelmådige spesialgrensesnitt.
|
|
|
|
Dette er en radikal forenkling av utviklingsoverflaten. I stedet for å
|
|
bygge og vedlikeholde chat-input, kanban-skjema, kalender-dialog,
|
|
notat-editor, voice-recorder, dagbok-felt — bygger vi *ett* grensesnitt
|
|
og investerer alt i å gjøre det feilfritt. Alt etterpå er edges.
|
|
|
|
### Visninger er spørringer mot grafen
|
|
Chat-visningen = "vis noder med edge til denne kanalen, sortert på tid."
|
|
Kanban-visningen = "vis noder med edge til dette boardet, gruppert på status."
|
|
Kalender-visningen = "vis noder med dato-edge, plassert på tidslinje."
|
|
Dagbok-visningen = "vis private noder for denne brukeren, sortert på tid."
|
|
|
|
Alle visninger leser fra samme graf. Ingen har "sin egen" data.
|
|
|
|
### To versjoner passer to-lags-modellen
|
|
Sanntidsversjonen lever i SpacetimeDB-laget: input streames, resultater er
|
|
live, andre ser hva du gjør. Flat versjonen lever i det tradisjonelle laget:
|
|
input sendes, lagres i PG, ferdig. Begge produserer identiske noder i grafen.
|
|
|
|
### Synlighet er bare en edge
|
|
Privat = edge kun til deg. Delt = edge til en gruppe/kanal. Publisert = edge
|
|
til en offentlig kontekst. Å "dele" noe er å legge til en edge. Å "gjøre
|
|
privat" er å fjerne den. Innholdet endres aldri.
|
|
|
|
## Universelt mottak — den andre primitiven
|
|
|
|
Input-primitiven er halvparten. Den andre halvdelen er *mottak*: hvordan
|
|
du konsumerer det andre produserer. Der input er "én overflate som fanger
|
|
alt", er mottak "én overflate som presenterer alt tilpasset *deg*."
|
|
|
|
### Dimensjoner ved mottak
|
|
|
|
**Format.** Lyd, tekst, visuelt — mottaker bestemmer (allerede beskrevet
|
|
over). Men det gjelder alt, ikke bare meldinger: en AI-oppsummering kan
|
|
leses eller høres. Et whiteboard-snapshot kan vises som bilde eller som
|
|
tekstlig beskrivelse.
|
|
|
|
**Filtrering.** Hva ser du? Alt fra alle er støy. Mottaksflaten filtrerer
|
|
basert på dine edges: hvilke kanaler du følger, hvilke personer du
|
|
samarbeider med, hvilke topics du er interessert i. Du kuraterer ikke
|
|
manuelt — du justerer edges, og mottaksflaten oppdateres.
|
|
|
|
**Prioritering.** Hva er viktig *nå*? En AI-assistert redaksjonell flate
|
|
som løfter frem det som trenger oppmerksomhet: ubesvarte meldinger,
|
|
oppgaver med frist, noder som er endret siden sist, tråder med aktivitet.
|
|
Ikke en notifikasjonsliste — en *vektet visning* av det som er relevant.
|
|
|
|
**Tempo.** Sanntid eller asynkront. I sanntidslaget: ting streamer inn
|
|
mens de skjer — en kollega snakker, du hører/leser live. I det
|
|
tradisjonelle laget: du får en digest, en oppsummering, et overblikk
|
|
over hva som har skjedd siden sist. Samme noder, ulikt tempo.
|
|
|
|
**Kilde.** Direkte fra en person, eller via en node. Et møte som
|
|
genererer innsikter. En AI-jobb som er ferdig. En tråd som har blitt
|
|
aktiv igjen. En podcast-episode som er klar for review. Kilden trenger
|
|
ikke være et menneske — det kan være en prosess, en hendelse, en
|
|
tilstandsendring i grafen.
|
|
|
|
### Mottaksflaten som speilbilde av input
|
|
|
|
| Input | Mottak |
|
|
|-------|--------|
|
|
| Én overflate for all input | Én overflate for alt mottak |
|
|
| Sender bestemmer ikke format | Mottaker bestemmer format |
|
|
| Modalitet er ortogonal | Presentasjon er ortogonal |
|
|
| Kontekst gir edges | Preferanser gir filtrering |
|
|
| Sanntid + flat versjon | Sanntid (stream) + asynkron (digest) |
|
|
|
|
### Mange-til-én og mange-via-mange
|
|
Mottaksflaten håndterer naturlig:
|
|
- **Én-til-én** — Trond sender deg en melding
|
|
- **Mange-til-én** — fem personer i en kanal, du ser alt
|
|
- **Via node** — et møte genererer et referat, du mottar det
|
|
- **Via kjede** — en chatmelding → blir oppgave → oppgaven fullføres →
|
|
du får oppdatering. Hele kjeden er edges, og du ser resultatet i din
|
|
mottaksflate uten å ha fulgt hvert steg.
|
|
|
|
### Mottaksflaten er også en visning av grafen
|
|
Akkurat som chat-visningen er "noder med kanal-edge sortert på tid", er
|
|
mottaksflaten "noder med edge til *meg*, vektet på relevans og tid."
|
|
Det er ikke en egen mekanisme — det er enda en spørring mot samme graf,
|
|
bare med *deg* som sentrum.
|
|
|
|
## Kommunikasjonsnoden — den tredje primitiven
|
|
|
|
Input fanger. Mottak presenterer. Men det mangler noe: *stedet* der folk
|
|
møtes. En kommunikasjonsnode er en node i grafen som samler deltakere,
|
|
definerer tilgangsregler, og fungerer som kontekst for input og mottak.
|
|
|
|
### Én node, mange former
|
|
|
|
En kommunikasjonsnode er konseptuelt identisk uansett skala:
|
|
|
|
| Variant | Deltakere | Input-tilgang | Mottak-tilgang |
|
|
|---------|-----------|---------------|----------------|
|
|
| Én-til-én samtale | 2 | Begge | Begge |
|
|
| Gruppechat | N | Alle medlemmer | Alle medlemmer |
|
|
| Redaksjonsmøte | N | Alle medlemmer | Alle medlemmer |
|
|
| Allmøte | 1 + N | Lederen snakker | Alle lytter, noen kan rekke opp hånden |
|
|
| Podcastinnspilling | 2-4 + N | Vertene snakker | Alle lytter, markører for produsent |
|
|
| Livesending | 1-4 + ∞ | Vertene | Streamet til nettside/app, lyd eller video |
|
|
| Asynkron gjest | 1 + 1 | Gjest gir input innen frist | Redaksjonen mottar |
|
|
|
|
Forskjellen er *ikke* ulike systemer — det er ulike edge-konfigurasjoner
|
|
på samme nodetype:
|
|
- **Eier-edge** — hvem kontrollerer noden (kan invitere, endre regler, avslutte)
|
|
- **Input-edge** — hvem kan gi input (snakke, skrive, tegne, dele skjerm)
|
|
- **Mottak-edge** — hvem kan motta (lytte, lese, se stream)
|
|
- **Rolle-edge** — spesialroller (moderator, produsent, gjest)
|
|
|
|
### Kommunikasjonsnoden er en kontekst for de andre primitivene
|
|
|
|
Når du gir input *i* en kommunikasjonsnode, arver inputen kontekst-edges
|
|
automatisk. Sier du noe i et møte → noden du skaper får edge til møtet.
|
|
Du trenger ikke tenke på det — konteksten følger med.
|
|
|
|
Mottak i en kommunikasjonsnode er det samme som universelt mottak, bare
|
|
scoped til den noden: du ser/hører det andre deltakere gir som input,
|
|
presentert etter dine preferanser.
|
|
|
|
### Livssyklus
|
|
|
|
En kommunikasjonsnode kan være:
|
|
- **Live** — aktiv i sanntidslaget. Deltakere er til stede, input streames.
|
|
- **Asynkron** — aktiv i det tradisjonelle laget. Deltakere gir input i
|
|
eget tempo (chat, asynkron gjest).
|
|
- **Avsluttet** — arkivert i PG. Alt som ble sagt/delt er noder med edges
|
|
til kommunikasjonsnoden. Kan søkes, gjenfinnes, refereres.
|
|
- **Gjenåpnet** — løftet tilbake til sanntidslaget. "Vi tar opp tråden
|
|
fra forrige møte" er bokstavelig talt å reaktivere en node.
|
|
|
|
### Skalering er en edge-endring, ikke en migrasjonsoperasjon
|
|
|
|
En samtale mellom to blir et møte ved å legge til flere deltaker-edges.
|
|
Et møte blir en livesending ved å legge til offentlige mottak-edges.
|
|
En livesending blir en podcast ved å legge til publiserings-edges på
|
|
arkivert innhold. Ingen migrering, ingen konvertering — bare edges.
|
|
|
|
## Tekniske forutsetninger
|
|
|
|
### STT (tale → tekst): løst
|
|
Faster-whisper kjører lokalt, god norsk kvalitet. Allerede i stacken.
|
|
|
|
### TTS (tekst → tale): løsbart
|
|
Norsk TTS lokalt er ikke godt nok ennå (Piper er "usable" men dårlig rytme,
|
|
XTTS-v2 støtter ikke norsk). Kommersielt er det løst:
|
|
- **ElevenLabs** — beste kvalitet, eksplisitt norsk med regionale aksenter
|
|
- **Azure Neural TTS** — god kvalitet, ~$15/M tegn
|
|
- **Google Cloud TTS** — god kvalitet, WaveNet/Neural2
|
|
|
|
Strategi: start med kommersiell API (ElevenLabs) bak AI Gateway, bytt til
|
|
lokal modell (Chatterbox Multilingual el.l.) når kvaliteten er god nok.
|
|
Brukeren merker ingenting — det er en backend-swap bak gatewayen. Samme
|
|
mønster som Whisper: tung jobb → jobbkø → worker → resultat som node-metadata.
|
|
|
|
Følg med på: **Chatterbox Multilingual** (Resemble AI) — annonsert norsk
|
|
støtte, 350M params, lovende for lokal kjøring.
|
|
|
|
## Spenninger og åpne spørsmål
|
|
|
|
- **Ytelse.** Én tabell med *alt* i — skalerer det? PG med riktige indekser
|
|
og partisjonering håndterer mye, men det er en reell designbeslutning.
|
|
- **Skjema.** Noder trenger noe felles skjema (innhold, created_at, author).
|
|
Men ulike modaliteter har ulike metadata (transkripsjon, bildestørrelse,
|
|
varighet). Hva er felles og hva er edge-metadata?
|
|
- **AI-klassifisering.** Når brukeren bare "sier noe" — hvem bestemmer hvilke
|
|
edges som knyttes? Manuelt? AI-foreslått? Kontekstbasert (sa det i en
|
|
kanal → kanal-edge)? Sannsynligvis en blanding, men det krever gjennomtenkt
|
|
UX.
|
|
- **Migrering.** Meldingsboksen har allerede innhold. Kan vi migrere til
|
|
node+edge-modellen gradvis, eller er det et brudd?
|
|
- **Kompleksitet for utviklere.** "Alt er noder og edges" er konseptuelt rent,
|
|
men å bygge en kanban-visning som spørrer en graf er mer komplekst enn å
|
|
lese fra en `cards`-tabell. Er abstraksjonen verdt kompleksiteten?
|
|
|
|
## Tre primitiver, én graf
|
|
|
|
| Primitiv | Hva den gjør | Brukerens opplevelse |
|
|
|----------|-------------|---------------------|
|
|
| **Input** | Fanger alt — tekst, lyd, bilde, AI | Én overflate å snakke/skrive/tegne i |
|
|
| **Mottak** | Presenterer alt tilpasset deg | Én personlig flate med det som er relevant |
|
|
| **Kommunikasjon** | Samler folk med tilgangsregler | Et sted å være — samtale, møte, sending |
|
|
|
|
Alt er noder og edges i samme graf. Input skaper noder. Mottak spør
|
|
grafen med deg som sentrum. Kommunikasjonsnoder gir kontekst og
|
|
tilgangsregler. Visninger (chat, kanban, kalender, dagbok, stream)
|
|
er bare spørringer med ulike filtre.
|
|
|
|
## Forhold til andre retninger
|
|
|
|
Denne retningen konkretiserer [rom, ikke forum](rom_ikke_forum.md):
|
|
- "Formløs input, struktur etterpå" → universell input + edges
|
|
- "To lag" → sanntidsversjon + flat versjon
|
|
- "Privat/delt som lag" → synlighet som edge
|
|
- "Siloer forsvinner" → alt er noder, visninger er spørringer
|
|
- "Rommet som primitiv" → kommunikasjonsnoden
|