Legg til ops/ (vedlikeholdsjobber) og docs/retninger/ (arkitektoniske teser)
Ny mappe ops/ med repeterbare vedlikeholdsjobber: - ryddejobb.md — full prosjektrevisjon - doc-audit.md — docs vs kode - drift-sjekk.md — prod vs lokal vs docs Ny mappe docs/retninger/ med arkitektoniske teser: - status_quo.md — hva Sidelinja er i dag - rom_ikke_forum.md — opplevelse-først, to-lags-modell, administrativ opplevelse - universell_input.md — tre primitiver (input, mottak, kommunikasjon), noder+edges - maskinrommet.md — Rust-orkestrator, edge-drevet ressursorkestrering, CAS+pruning - bruker_ikke_workspace.md — brukeren er sentrum, workspaces er samlings-noder - datalaget.md — PG+Apache AGE, SpacetimeDB som sanntidslag, lagmodell Oppdatert CLAUDE.md og proposals/README.md med referanser. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
17b2c44bdd
commit
8ca9832248
13 changed files with 1407 additions and 2 deletions
|
|
@ -51,6 +51,13 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
|||
- `synkronisering.md` — PostgreSQL ↔ SpacetimeDB dataflyt og eierskapsmodell
|
||||
- `api_grensesnitt.md` — Kommunikasjonskart: SvelteKit er web-API, Rust er worker
|
||||
- `ai_gateway.md` — LiteLLM som sentralisert AI-ruter (BYOK + OpenRouter fallback)
|
||||
- `docs/retninger/` — Store arkitektoniske teser og retningsspørsmål (se `README.md` for oversikt):
|
||||
- `status_quo.md` — Hva Sidelinja er i dag: ambisiøse primitiver, tradisjonell overflate
|
||||
- `rom_ikke_forum.md` — Bør Sidelinja være et sanntidsrom fremfor en tradisjonell webapp?
|
||||
- `universell_input.md` — Én multimodal input/mottak-primitiv, noder + edges i stedet for separate tabeller
|
||||
- `maskinrommet.md` — Rust-tjenestelaget: fang, prosesser, lever — fast grensesnitt for alle tekniske tjenester
|
||||
- `bruker_ikke_workspace.md` — Brukeren er sentrum, workspaces er frivillige samlings-noder
|
||||
- `datalaget.md` — PG + Apache AGE som enhetlig graf og arkiv, migreringsstrategi
|
||||
- `docs/proposals/` — Halvtenkte idéer og kreative innfall (se `README.md` for oversikt)
|
||||
- `docs/erfaringer/` — Lærdommer fra implementering (feller, anti-patterns, løsninger):
|
||||
- `svelte5_reaktivitet.md` — $state-getters, SSR-feller, polling-mønster
|
||||
|
|
@ -81,7 +88,7 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
|||
- Tunge AI-jobber (Whisper, LLM-kall) skal aldri blokkere web-requests
|
||||
- All AI-kode peker på `http://ai-gateway:4000/v1` — aldri direkte til leverandør-APIer
|
||||
- Kod og test lokalt i WSL2, deploy via push til Forgejo + SSH pull
|
||||
- Sjekk alltid relevant doc i `docs/concepts/`, `docs/features/` eller `docs/infra/` før du implementerer
|
||||
- Sjekk alltid relevant doc i `docs/concepts/`, `docs/features/`, `docs/infra/` eller `docs/retninger/` før du implementerer
|
||||
- Sjekk `docs/erfaringer/` for kjente feller før du implementerer med Svelte 5, SpacetimeDB eller adapter-mønsteret
|
||||
- Etter ferdig implementering av en komponent: dokumenter lærdommer i `docs/erfaringer/`
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ Halvtenkte idéer, kreative innfall og ting vi vil utforske når vi får tid. Ik
|
|||
## Pipeline
|
||||
|
||||
```
|
||||
retninger/ → påvirker alt (arkitektoniske teser)
|
||||
proposals/ → features/ eller concepts/
|
||||
(idé) (spesifisert, klar for implementering)
|
||||
```
|
||||
|
||||
Når en idé modnes nok til å bli implementert, skrives en full spec i `docs/features/` eller `docs/concepts/` og forslaget slettes herfra.
|
||||
Når en idé modnes nok til å bli implementert, skrives en full spec i `docs/features/` eller `docs/concepts/` og forslaget slettes herfra. Idéer som er for store og fundamentale for proposals — arkitektoniske teser om prosjektets retning — hører hjemme i `docs/retninger/`.
|
||||
|
||||
## Oversikt
|
||||
|
||||
|
|
|
|||
36
docs/retninger/README.md
Normal file
36
docs/retninger/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Retninger
|
||||
|
||||
Store, åpne spørsmål om prosjektets identitet og arkitektoniske retning.
|
||||
|
||||
Dette er ikke features, ikke proposals, ikke spesifikasjoner — det er **teser** som
|
||||
utforsker hvordan Sidelinja bør tenke om seg selv. En retning kan påvirke alt fra
|
||||
teknologivalg til UX-filosofi, men den er ikke en beslutning. Den er en pågående
|
||||
diskusjon.
|
||||
|
||||
## Pipeline
|
||||
|
||||
```
|
||||
retninger/ → kan informere alt:
|
||||
(tese) concepts/, features/, infra/, arkitektur.md
|
||||
```
|
||||
|
||||
En retning "forfremmes" ikke — den modnes, og det den konkluderer med påvirker
|
||||
andre dokumenter. En retning kan også forkastes eller parkeres.
|
||||
|
||||
## Oversikt
|
||||
|
||||
| Retning | Status | Kjernespørsmål |
|
||||
|---------|--------|----------------|
|
||||
| [Status quo](status_quo.md) | Referanse | Hva er Sidelinja i dag? Ankerpunkt for de andre retningene. |
|
||||
| [Rom, ikke forum](rom_ikke_forum.md) | Åpen | Bør Sidelinja være en oppslukende sanntidsopplevelse fremfor en tradisjonell webapp? |
|
||||
| [Universell input og mottak](universell_input.md) | Åpen | Én multimodal input-primitiv + personlig mottaksflate, noder + edges i stedet for separate tabeller |
|
||||
| [Maskinrommet](maskinrommet.md) | Åpen | Én Rust-tjeneste med fast grensesnitt for alle tekniske tjenester: fang, prosesser, lever |
|
||||
| [Bruker, ikke workspace](bruker_ikke_workspace.md) | Åpen | Brukeren er sentrum, workspaces er frivillige samlings-noder — ikke containere |
|
||||
| [Datalaget](datalaget.md) | Åpen | PG + Apache AGE som enhetlig graf og arkiv, SpacetimeDB som sanntidslag, CAS for binærdata |
|
||||
|
||||
## Format
|
||||
- Hva er tesen?
|
||||
- Hva motiverer den? (observasjoner, frustrasjoner, inspirasjon)
|
||||
- Hva ville vært annerledes hvis vi fulgte den?
|
||||
- Spenninger og åpne spørsmål
|
||||
- Ingen krav om konklusjon
|
||||
127
docs/retninger/bruker_ikke_workspace.md
Normal file
127
docs/retninger/bruker_ikke_workspace.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Bruker, ikke workspace
|
||||
|
||||
> Primærenheten er brukeren og brukerens edges. Workspaces er ikke
|
||||
> containere — de er frivillige samlings-noder som gir felles kontekst.
|
||||
|
||||
## Observasjoner
|
||||
|
||||
I dag er workspaces den organisatoriske enheten. Du "er i" et workspace,
|
||||
og det bestemmer hva du ser: kanaler, boards, kalendre. Vil du se noe
|
||||
fra et annet workspace må du bytte. Det er en container-modell — ting
|
||||
*bor* i workspaces.
|
||||
|
||||
Men i node+edge-modellen gir dette ikke mening. En node er ikke "i" noe
|
||||
— den har edges til ting. Og brukeren er ikke "i" et workspace — brukeren
|
||||
har edges til noder, personer, topics, samtaler.
|
||||
|
||||
## Tesen
|
||||
|
||||
**Brukeren er sentrum.** Du logger inn og ser dine edges — alt du er
|
||||
koblet til. Ikke "velg workspace" som første handling, men "her er alt
|
||||
ditt."
|
||||
|
||||
### Workspace som samlings-node, ikke container
|
||||
|
||||
Et workspace eksisterer fortsatt som konsept, men det er en node i
|
||||
grafen — ikke en organisatorisk boks:
|
||||
|
||||
- **Tradisjonell modell:** Workspace inneholder kanaler, boards, filer.
|
||||
Brukeren er "i" workspacet.
|
||||
- **Node-modell:** Workspace er en node. Noder har edge til den. Brukeren
|
||||
har edge til den. Workspace-noden bærer felles kontekst (tema,
|
||||
pruning-profil, AI-konfig).
|
||||
|
||||
En node kan ha edge til flere workspaces. En faktoid om en gjest er
|
||||
relevant for både podcast-prosjektet og research-samlingen. Ikke kopi —
|
||||
samme node, to edges.
|
||||
|
||||
### Personlig er default
|
||||
|
||||
Alt uten workspace-edge eller mottaker-edge er privat — tilhører bare
|
||||
deg. "Personlig workspace" er ikke en egen ting du oppretter. Det er
|
||||
fravær av deling. Dine private notater, dagbok, voice memos — de har
|
||||
bare edges til deg.
|
||||
|
||||
### Administrasjon er edges med roller
|
||||
|
||||
Brukerstyring forvaltes i nodene selv, ikke i et sentralt admin-panel:
|
||||
|
||||
| Edge-type | Hva den gir |
|
||||
|-----------|------------|
|
||||
| Eier-edge | Full kontroll — slette, endre tilgang, endre innstillinger |
|
||||
| Admin-edge | Kan invitere/fjerne andre, endre konfigurasjon |
|
||||
| Deltaker-edge | Kan gi input og motta |
|
||||
| Leser-edge | Kan kun motta (observatør, lytter) |
|
||||
|
||||
Du oppretter en kommunikasjonsnode (podcast-prosjekt). Du er eier
|
||||
automatisk. Du inviterer Trond → deltaker-edge. Trond kan gi input
|
||||
men ikke slette eller endre tilgang. Du gir Trond admin-edge → nå
|
||||
kan han invitere andre og endre innstillinger på den noden.
|
||||
|
||||
Dette skalerer fra en privat notat (kun eier-edge til deg) til en
|
||||
organisasjon (samlings-node med mange admin- og deltaker-edges).
|
||||
|
||||
### Brukeropplevelsen
|
||||
|
||||
Når du logger inn ser du:
|
||||
- **Dine aktive samtaler** — kommunikasjonsnoder med edge til deg
|
||||
- **Dine noder** — alt du har skapt eller er koblet til
|
||||
- **Dine samlings-noder** (det som før var workspaces) — grupperer
|
||||
kontekst, men du trenger ikke "gå inn i" dem
|
||||
- **Din mottaksflate** — alt som er relevant for deg nå, vektet
|
||||
|
||||
Du kan filtrere etter samlings-node hvis du vil fokusere ("vis bare
|
||||
podcast-prosjektet"), men det er et filter — ikke en modebytte.
|
||||
|
||||
### Felles kontekst på samlings-noder
|
||||
|
||||
En samlings-node (workspace) bærer kontekst som arves av tilknyttede
|
||||
noder:
|
||||
|
||||
- **Pruning-profil** — hvor aggressivt slettes binærdata?
|
||||
- **Tema** — visuelt uttrykk (CSS custom properties)
|
||||
- **AI-konfigurasjon** — hvilke prompts, hvilken modell, hvilke regler
|
||||
- **Tilgangsnivå** — default synlighet for nye noder opprettet i
|
||||
denne konteksten
|
||||
- **Kapasitet** — ressursgrenser for maskinrommet (f.eks. maks
|
||||
samtidige transkripsjoner)
|
||||
|
||||
## Implikasjoner
|
||||
|
||||
### Kryssgående noder er naturlig
|
||||
En node med edge til to samlings-noder arver kontekst fra begge.
|
||||
Konflikter (ulik pruning-profil) løses med prioriteringsregler:
|
||||
mest konservativ vinner, eller eier-edge bestemmer.
|
||||
|
||||
### Onboarding forenkles
|
||||
Ny bruker trenger ikke "settes opp i et workspace." De får edges til
|
||||
de nodene de trenger tilgang til. Ferdig. Endre tilgang = endre edges.
|
||||
|
||||
### Forlate et prosjekt er å fjerne edges
|
||||
Ingen "slett bruker fra workspace." Fjern deltaker-edges. Brukerens
|
||||
private noder som hadde edge til samlings-noden beholder den edgen
|
||||
(det er brukerens innhold), men de mister tilgang til andres noder.
|
||||
|
||||
## Spenninger og åpne spørsmål
|
||||
|
||||
- **Overblikk.** Uten workspaces som organisatorisk enhet — hvordan
|
||||
unngår du at alt flyter sammen? Samlings-noder er svaret, men de
|
||||
må være intuitive å opprette og bruke uten å bli "workspaces med
|
||||
ny navn."
|
||||
- **Tilgangskontroll i praksis.** Edge-basert tilgang er fleksibelt,
|
||||
men kan det bli uoversiktlig? "Hvem har tilgang til hva" må være
|
||||
lett å besvare. Kanskje samlings-noden gir en naturlig audit-visning.
|
||||
- **Arv og konflikter.** En node med edge til to samlings-noder med
|
||||
ulike pruning-profiler — hva vinner? Trenger klare regler som er
|
||||
intuitive for brukeren.
|
||||
- **Migrering.** Eksisterende workspace-modell har innhold. Kan
|
||||
workspaces bli samlings-noder gradvis?
|
||||
|
||||
## Forhold til andre retninger
|
||||
|
||||
- [Rom, ikke forum](rom_ikke_forum.md) — "rommet" er ikke et workspace,
|
||||
det er summen av dine edges
|
||||
- [Universell input og mottak](universell_input.md) — mottaksflaten er
|
||||
"noder med edge til *meg*", ikke "noder i *mitt workspace*"
|
||||
- [Maskinrommet](maskinrommet.md) — leser samlings-node-edges for
|
||||
kontekst (pruning, kapasitet, AI-konfig)
|
||||
175
docs/retninger/datalaget.md
Normal file
175
docs/retninger/datalaget.md
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# Datalaget — PG + AGE som enhetlig graf og arkiv
|
||||
|
||||
> PostgreSQL er den eneste databasen. Apache AGE gir Cypher-semantikk
|
||||
> for graftraverseringer. SpacetimeDB er sanntidslaget over samme data.
|
||||
> Én database, to tilgangsmønstre, ingen eierskapskonflikt.
|
||||
|
||||
## Observasjoner
|
||||
|
||||
Hele retningsarbeidet har bygget seg mot "alt er noder og edges."
|
||||
Tilgang er edges, visninger er spørringer, administrasjon er
|
||||
traversering. Spørsmålet er: hvilken teknologi støtter dette best?
|
||||
|
||||
### Neo4j ble vurdert og forkastet
|
||||
|
||||
Neo4j er best-in-class for dype graftraverseringer, men for dette
|
||||
prosjektet gir den mer problemer enn den løser:
|
||||
|
||||
- **Tredje database.** PG + SpacetimeDB + Neo4j = tre systemer å
|
||||
drifte, sikkerhetskopiere og overvåke. På én Hetzner VPS.
|
||||
- **Mister PG-økosystemet.** pgvector (semantisk søk), fulltekstsøk,
|
||||
JSONB, pg_cron, statistikk-aggregeringer — alt dette trenger vi
|
||||
uansett, og det lever i PG. Med Neo4j trenger vi PG *i tillegg*.
|
||||
- **Minnehungrig.** Neo4j anbefaler 8-16GB heap. På en delt VPS med
|
||||
PG, SpacetimeDB, Whisper og LiteLLM er det for mye.
|
||||
- **Lisens.** Community er AGPLv3. Enterprise-features (clustering,
|
||||
avansert sikkerhet) krever betalt lisens.
|
||||
|
||||
### Apache AGE — Cypher i PG
|
||||
|
||||
Apache AGE er en PG-extension som legger til openCypher-støtte. Noder
|
||||
og edges lagres i PG-tabeller, men spørres med Cypher-semantikk.
|
||||
|
||||
**Oppsider:**
|
||||
- **Én database.** Ingen ny infrastruktur. Samme backup, connection
|
||||
pooling, tooling, monitoring.
|
||||
- **SQL + Cypher i samme spørring.** "Finn alle noder brukeren har
|
||||
tilgang til via team-traversering, JOINet med fulltekstsøk og
|
||||
pgvector." Det kan du ikke gjøre med en separat grafdatabase.
|
||||
- **Null migrasjonskostnad.** Eksisterende nodes/edges-tabeller kan
|
||||
eksponeres som graf. Legg til extension, ikke bytt database.
|
||||
- **Økosystemet bevares.** pgvector, fulltekstsøk, JSONB, pg_cron —
|
||||
alt fungerer side om side med Cypher-spørringer.
|
||||
- **Produksjonsbruk.** Azure og EDB tilbyr AGE som managed extension.
|
||||
|
||||
**Nedsider:**
|
||||
- **Ikke native graf.** Under panseret er det PG-tabeller. For svært
|
||||
dype traverseringer (10+ hopp over millioner av noder) er Neo4j
|
||||
raskere. Men de fleste spørringene våre er 1-5 hopp.
|
||||
- **Yngre prosjekt.** Mindre community, tynnere dokumentasjon enn Neo4j.
|
||||
- **Quirks.** Spørringer må være enten read-only eller write-only
|
||||
(WITH-clause for overgang). Noen pg_upgrade-begrensninger.
|
||||
|
||||
## Beslutning
|
||||
|
||||
**PostgreSQL + Apache AGE** som enhetlig datalager for noder og edges.
|
||||
|
||||
De fleste spørringer i dette systemet er grunne:
|
||||
- "Vis noder med edge til denne brukeren" — 1 hopp
|
||||
- "Sjekk tilgang via team" — 2 hopp
|
||||
- "Finn alle kommunikasjonsnoder brukeren har tilgang til" — 2-3 hopp
|
||||
- "Traverser kunnskapsgrafen mellom to emner" — 3-5 hopp
|
||||
|
||||
AGE håndterer dette. Skulle det vise seg at dypere traverseringer
|
||||
blir en flaskehals *i praksis*, kan Neo4j evalueres da — men med
|
||||
én VPS og realistiske datamengder er det usannsynlig.
|
||||
|
||||
## Lagmodell
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ GUI (SvelteKit) │
|
||||
│ Primitiver, visninger │
|
||||
└────────┬──────────────────┬─────────┘
|
||||
│ skriv │ les (sanntid)
|
||||
│ │ direkte WebSocket
|
||||
┌────────▼────────┐ ┌─────▼─────────┐
|
||||
│ Maskinrommet │ │ SpacetimeDB │──┐
|
||||
│ (Rust) │ │ (STDB) │ │
|
||||
│ Orkestrering │ └───────────────┘ │
|
||||
└──┬─────┬─────┬──┘ │
|
||||
│ │ │ sync ↕ │
|
||||
▼ ▼ ▼ │
|
||||
┌─────┐┌─────┐┌─────┐┌─────────────┐ │
|
||||
│ PG ││STDB ││ CAS ││ Whisper, │ │
|
||||
│+AGE ││(skr)││ ││ LiteLLM, │ │
|
||||
│ ││ ││ ││ LiveKit ... │ │
|
||||
└─────┘└─────┘└─────┘└─────────────┘ │
|
||||
```
|
||||
|
||||
### Skriv vs les — to stier med god grunn
|
||||
|
||||
**Skriv:** GUI → Maskinrommet (Rust) → tjenester (PG, STDB, CAS, ...)
|
||||
|
||||
All orkestrering, edge-logikk, ressursallokering og validering går
|
||||
gjennom Rust. Maskinrommet bestemmer hva som skjer: hva som skrives
|
||||
til PG, hva som speiles til SpacetimeDB, hva som lagres i CAS,
|
||||
hvilke tjenester som trigges.
|
||||
|
||||
**Les (sanntid):** SpacetimeDB → GUI (direkte WebSocket)
|
||||
|
||||
SpacetimeDB sitt klient-SDK kobler seg direkte via WebSocket,
|
||||
definerer SQL-subscriptions, og synkroniserer automatisk med lokal
|
||||
cache — uten network round-trips for lesing (~10μs per transaksjon).
|
||||
Å proxy dette gjennom Rust ville lagt til et hopp, serialisering og
|
||||
kontekstbytte — en dårligere reimplementering av noe STDB gjør
|
||||
optimalt. Den direkte lese-stien er en bevisst arkitekturbeslutning,
|
||||
ikke et hull i lagmodellen.
|
||||
|
||||
**Les (tradisjonelt):** GUI → Maskinrommet (Rust) → PG
|
||||
|
||||
Søk, historikk, statistikk, arkiv — alt som ikke er sanntid går
|
||||
gjennom Rust og PG. AGE-spørringer for graftraversering, pgvector
|
||||
for semantisk søk, SQL for aggregeringer.
|
||||
|
||||
### PG er arkivet og grafen
|
||||
Alle noder og edges lever i PG. AGE gir Cypher-spørringer for
|
||||
traversering. Standard SQL for alt annet (aggregeringer, fulltekstsøk,
|
||||
JOINs mot pgvector). Én database, to spørrespråk som utfyller
|
||||
hverandre.
|
||||
|
||||
### SpacetimeDB er sanntidslaget
|
||||
Aktive noder og edges speilet til SpacetimeDB for live-oppdateringer.
|
||||
Ting som er "nå" lever i SpacetimeDB. Ting som er "ferdig" lever kun
|
||||
i PG. Ingen eierskapskonflikt — SpacetimeDB er en live-visning av
|
||||
en delmengde av PG-grafen.
|
||||
|
||||
### CAS er binærlageret
|
||||
Lyd, bilde, video lagres content-addressable utenfor PG. Noder i
|
||||
PG peker på CAS-hasher. Pruning-regler basert på edges, aksesslog
|
||||
og modalitet (se maskinrommet).
|
||||
|
||||
## Migreringsstrategi
|
||||
|
||||
### Fase 1: AGE på eksisterende PG
|
||||
Installer Apache AGE extension. Eksponer eksisterende nodes/edges-
|
||||
tabeller som graf. Ingen endring for resten av systemet — bare en
|
||||
ny måte å spørre på.
|
||||
|
||||
### Fase 2: Konsolider til node+edge-modellen
|
||||
Migrer meldingsboks, kanban, kalender etc. til den universelle
|
||||
node+edge-modellen gradvis. Gamle tabeller kan leve som views
|
||||
over grafen i overgangsperioden.
|
||||
|
||||
### Fase 3: Parallell prototype
|
||||
Det gamle systemet kjører uforstyrret. En ny frontend-prototype
|
||||
bygges ved siden av som bruker AGE-grafen direkte. Live sync fra
|
||||
gammelt til nytt via enveis oppdateringer. Testbrukere leker i
|
||||
det nye, produksjon er trygt i det gamle.
|
||||
|
||||
### Fase 4: Cutover
|
||||
Når det nye er godt nok, flyttes trafikk over. Det gamle systemet
|
||||
kan kjøre som read-only fallback en periode.
|
||||
|
||||
## Spenninger og åpne spørsmål
|
||||
|
||||
- **AGE-modenhet.** Prosjektet er aktivt og støttet av Azure/EDB,
|
||||
men community er mindre enn Neo4j. Risikoen er håndterbar fordi
|
||||
dataene alltid er PG-tabeller — AGE er bare et spørrelag.
|
||||
- **SpacetimeDB-synk.** Hvordan synkes noder/edges mellom PG (AGE)
|
||||
og SpacetimeDB effektivt? Trenger en sync-mekanisme som forstår
|
||||
"aktiv delmengde."
|
||||
- **Skjemadesign.** Én node-tabell med alle typer innhold — hva er
|
||||
felles kolonner vs edge-metadata vs JSONB-payload? Trenger
|
||||
gjennomtenkt skjema som balanserer fleksibilitet og spørrbarhet.
|
||||
|
||||
## Forhold til andre retninger
|
||||
|
||||
- [Universell input og mottak](universell_input.md) — noder og edges
|
||||
er den underliggende datamodellen for alle tre primitiver
|
||||
- [Maskinrommet](maskinrommet.md) — CAS og pruning lever her, AGE
|
||||
brukes for edge-drevet ressursorkestrering
|
||||
- [Bruker, ikke workspace](bruker_ikke_workspace.md) — tilgang via
|
||||
graf-traversering (AGE) i stedet for workspace-membership-tabeller
|
||||
- [Rom, ikke forum](rom_ikke_forum.md) — to-lags-modellen:
|
||||
SpacetimeDB for nåtid, PG+AGE for arkiv og graf
|
||||
301
docs/retninger/maskinrommet.md
Normal file
301
docs/retninger/maskinrommet.md
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
# Maskinrommet — teknisk tjenestelaget
|
||||
|
||||
> Én Rust-tjeneste med et fast grensesnitt. Alle tekniske tjenester beveger
|
||||
> seg gjennom dette laget. Fang, prosesser, lever.
|
||||
|
||||
## Observasjoner
|
||||
|
||||
I dag er tekniske tjenester spredt:
|
||||
- **Worker** (Rust) — kjører bakgrunnsjobber
|
||||
- **Jobbkø** (PG) — koordinerer arbeid
|
||||
- **AI Gateway** (LiteLLM) — ruter AI-kall
|
||||
- **Whisper** — transkripsjon
|
||||
- **LiveKit** — lyd/video-strømmer
|
||||
|
||||
Hver har sitt eget grensesnitt. Frontend og primitiv-laget må vite hva
|
||||
som finnes under panseret. Det er ingen felles abstraksjon, ingen felles
|
||||
logging, ingen felles kapasitetsstyring.
|
||||
|
||||
## Tesen
|
||||
|
||||
Alt som krever tunge ressurser eller eksterne tjenester går gjennom
|
||||
**ett lag** med **ett grensesnitt**. Ikke fordi det er elegant — fordi
|
||||
det gir et fast punkt som er enkelt å fange, modifisere og forbedre.
|
||||
|
||||
Maskinrommet gjør tre ting:
|
||||
|
||||
### 1. Fang (input-absorpsjon)
|
||||
Ta imot råmateriale i alle modaliteter:
|
||||
- Tekst (melding, URL, dokument)
|
||||
- Lyd (voice memo, live stream, filopplasting)
|
||||
- Bilde (foto, skjermbilde, tegning)
|
||||
- Video (stream, opptak)
|
||||
- Strukturert data (JSON, metadata, edges)
|
||||
|
||||
### 2. Prosesser (transformasjon)
|
||||
Analyser, transformer, berik og systematiser:
|
||||
- **STT** — lyd → tekst (Whisper)
|
||||
- **TTS** — tekst → lyd (ElevenLabs / lokal modell)
|
||||
- **AI-analyse** — oppsummering, klassifisering, sentimentanalyse,
|
||||
faktasjekk, edge-forslag
|
||||
- **Beriking** — URL → metadata, bilde → beskrivelse, lyd → segmenter
|
||||
- **Søk** — fulltekst, semantisk (pgvector), graftraversering
|
||||
- **Mediaprosessering** — transcode, thumbnail, waveform
|
||||
|
||||
### 3. Lever (output-distribusjon)
|
||||
Lever resultat i riktig modalitet til riktig mottaker:
|
||||
- Tekst (melding, notifikasjon, digest)
|
||||
- Lyd (TTS-opplesning, lydstream)
|
||||
- Video/bilde (stream, thumbnail, snapshot)
|
||||
- Strukturert data (noder, edges, metadata tilbake i grafen)
|
||||
- Push (webhook, SSE, SpacetimeDB-reducer)
|
||||
|
||||
## Edge-drevet ressursorkestrering
|
||||
|
||||
Nøkkelinnsikten: **maskinrommet leser edges for å vite hva det skal gjøre.**
|
||||
Noden selv er alltid enkel. Det er edgene som bestemmer hvilke ressurser
|
||||
som spinnes opp.
|
||||
|
||||
### Security by default
|
||||
Input uten mottaker-edge er automatisk privat. Du trenger ikke "velge
|
||||
privat" — det er utgangspunktet. Ingen ser det. Ingen ressurser kobles
|
||||
inn utover det grunnleggende (fang + transkriber). Privat er ikke en
|
||||
innstilling, det er fravær av deling.
|
||||
|
||||
### Ressurser er proporsjonale med edges
|
||||
Samme nodetype, vilt forskjellig ressursbruk:
|
||||
|
||||
```
|
||||
Dagboknotat (privat voice memo):
|
||||
node → fang lyd → transkriber (Whisper) → lagre
|
||||
Ressurser: minimal
|
||||
|
||||
Samtale med Trond:
|
||||
node + mottaker-edge(Trond)
|
||||
→ fang lyd → transkriber → lever tekst/lyd til Trond
|
||||
Ressurser: STT + levering til én
|
||||
|
||||
Redaksjonsmøte (5 deltakere):
|
||||
node + mottaker-edges(5) + rolle-edges
|
||||
→ fang lyd fra alle → transkriber → lever til alle → AI-referent
|
||||
Ressurser: STT + levering til 5 + LLM
|
||||
|
||||
Livesending (1000 lyttere):
|
||||
node + mottaker-edges(∞) + stream-edge + publiserings-edge
|
||||
→ fang lyd → transkriber → stream via LiveKit → distribuer
|
||||
→ generer segmenter → kjør live AI → publiser
|
||||
Ressurser: STT + LiveKit + LLM + mediaprosessering
|
||||
```
|
||||
|
||||
Maskinrommet gjør ikke mer enn det edges krever. Ingen overhead for
|
||||
enkle ting. Noden vet ingenting om LiveKit — den har bare edges som
|
||||
sier "stream til disse mottakerne", og maskinrommet bestemmer at det
|
||||
betyr LiveKit.
|
||||
|
||||
### Naturlig eskalering
|
||||
Du starter en privat voice-note. Bestemmer deg for å dele den med Trond
|
||||
→ legg til mottaker-edge, maskinrommet begynner å levere. Trond foreslår
|
||||
at dere tar det som et møte → legg til flere deltaker-edges, maskinrommet
|
||||
kobler inn sanntidsstrømming. Møtet blir en innspilling → legg til
|
||||
publiserings-edge, maskinrommet aktiverer produksjonspipeline.
|
||||
|
||||
Hvert steg er bare å legge til edges. Maskinrommet reagerer og kobler
|
||||
inn flere ressurser etter hvert. Ingen migrering, ingen modebytte.
|
||||
|
||||
## Grensesnittet
|
||||
|
||||
Maskinrommet eksponerer et konsistent API — sannsynligvis en Rust trait
|
||||
eller et sett traits:
|
||||
|
||||
```
|
||||
fang(input: RåInput) → NodeId
|
||||
prosesser(node: NodeId, operasjon: Operasjon) → Resultat
|
||||
lever(node: NodeId, mottaker: Mottaker, format: Format) → Status
|
||||
```
|
||||
|
||||
Men i praksis er mye av dette *reaktivt*: maskinrommet observerer
|
||||
edge-endringer og handler automatisk. Legger noen til en mottaker-edge
|
||||
→ maskinrommet begynner å levere. Legger noen til en stream-edge →
|
||||
maskinrommet kobler inn LiveKit. Primitivene trenger ikke eksplisitt
|
||||
kalle `lever()` — de manipulerer edges, og maskinrommet reagerer.
|
||||
|
||||
## Hva dette gir
|
||||
|
||||
### Isolasjon
|
||||
Bytt Whisper med noe annet? Endre maskinrommet. Frontend vet ingenting.
|
||||
Legg til bildegenerering? Ny operasjon i maskinrommet. Primitivene
|
||||
kaller den uten å vite hva som skjer under.
|
||||
|
||||
### Observerbarhet
|
||||
Alt går gjennom ett punkt. Logging, metrikker, kostnadsrapportering,
|
||||
feilhåndtering — alt på ett sted. "Hva bruker vi AI-ressurser på?"
|
||||
har ett svar.
|
||||
|
||||
### Kapasitetsstyring
|
||||
Prioritering, kø, rate limiting, fallback mellom leverandører — alt
|
||||
håndtert av maskinrommet. En podcastinnspilling som trenger live
|
||||
transkripsjon kan prioriteres over en bakgrunns-oppsummering.
|
||||
|
||||
### Fast utviklingspunkt
|
||||
To team (eller to hatter) med klart grensesnitt:
|
||||
- **Over maskinrommet:** primitiver, noder, edges, UI, brukeropplevelse
|
||||
- **I maskinrommet:** ytelse, integrasjoner, kapasitet, kostnad
|
||||
|
||||
Du kan perfeksjonere det ene uten å røre det andre.
|
||||
|
||||
## Content-Addressable Storage og intelligent pruning
|
||||
|
||||
Maskinrommet forvalter også lagring. Ikke alt kan lagres for evig — men
|
||||
ikke alt trenger det heller. Signalene for hva som er viktig finnes
|
||||
allerede i grafen.
|
||||
|
||||
### CAS som lagringsprimitiv
|
||||
All binærdata (lyd, bilde, video) lagres i et content-addressable store.
|
||||
Fordeler:
|
||||
- **Deduplisering gratis** — samme fil delt i tre kontekster = én kopi
|
||||
- **Separasjon** — "innholdet eksisterer" er adskilt fra "innholdet er
|
||||
tilgjengelig." Noden peker på en hash, CAS har filen (eller ikke).
|
||||
- **Enkel opprydning** — slett hashen fra CAS, alle noder som pekte
|
||||
dit mister binærdataen men beholder metadata og transkripsjon.
|
||||
|
||||
### Lagringsregler per modalitet
|
||||
|
||||
| Modalitet | Default levetid | Begrunnelse |
|
||||
|-----------|----------------|-------------|
|
||||
| Tekst | Evig | Billig, er essensen av innholdet |
|
||||
| Transkripsjon | Evig | Tekstlig representasjon av lyd/video — tar vare på meningen |
|
||||
| Lyd | 30 dager | Mellomkostnad, transkripsjon bevarer innholdet |
|
||||
| Bilde | 30 dager | Mellomkostnad, beskrivelse/metadata bevarer kontekst |
|
||||
| Video | 14 dager | Dyrest, transkripsjon + thumbnail bevarer det meste |
|
||||
|
||||
### Signaler som forlenger levetid
|
||||
|
||||
Default-TTL er bare utgangspunktet. Maskinrommet justerer basert på:
|
||||
|
||||
- **Edges.** En lydfil med edge til episoderegisteret = publisert
|
||||
podcast, beholdes. En privat voice-memo uten edges = 30-dagers TTL.
|
||||
- **Aksesslog.** Hvis noen har spilt av lydfilen i løpet av TTL-perioden,
|
||||
forlenges den. Ingen aksess = ingen verdi i å beholde binærdataen.
|
||||
- **Transkripsjonsstatus.** Lyd som er transkribert har "overlevert sin
|
||||
essens" til tekst. Lyd som *ikke* er transkribert (f.eks. musikk,
|
||||
lydeffekter) kan trenge lengre TTL.
|
||||
- **Edge-type.** Edge til publisert innhold = behold. Edge til arkivert
|
||||
møte = transkripsjon holder. Edge til ingenting = teksten lever videre,
|
||||
binærdataen kan dø.
|
||||
|
||||
### Eksempler
|
||||
|
||||
```
|
||||
Privat voice-memo, aldri delt:
|
||||
→ Lyd transkriberes → tekst lagres evig
|
||||
→ Lydfil: 30 dager, ingen aksess, ingen edges → slettes
|
||||
→ Noden lever videre med teksten
|
||||
|
||||
Podcastepisode:
|
||||
→ Lyd har edge til episoderegister + publiserings-edge
|
||||
→ Aksesseres regelmessig via podcastarkivet
|
||||
→ Lydfil: beholdes så lenge edges og aksess tilsier det
|
||||
|
||||
Rutinemøte for et år siden:
|
||||
→ Video (6 kanaler): ingen har sett den på 6 måneder → slettes
|
||||
→ Lyd: ingen har spilt av → slettes
|
||||
→ Transkripsjon: tekst, lagres evig. Søkbar, refererbar.
|
||||
→ Noden lever med full kontekst minus binærdata
|
||||
|
||||
Viktig styremøte:
|
||||
→ Video aksesseres av styremedlemmer → forlenges
|
||||
→ Workspace-innstilling: "behold video i 1 år" → overrider default
|
||||
```
|
||||
|
||||
### Generert innhold er en cache
|
||||
TTS, thumbnails, AI-oppsummeringer, waveforms — alt som kan regenereres
|
||||
fra kildedata er i praksis en cache. Det lagres i CAS med samme TTL-
|
||||
mekanisme som alt annet:
|
||||
- Peter ber om lyd-versjon av en tekstmelding → TTS genereres, lagres
|
||||
- Ingen spiller den av på 30 dager → filen slettes fra CAS
|
||||
- Peter (eller noen andre) ber om lyd igjen → regenereres on-demand
|
||||
- Teksten er der alltid. Binærdataen er flyktig.
|
||||
|
||||
Maskinrommet trenger ikke skille mellom "original lyd" (voice memo) og
|
||||
"generert lyd" (TTS) i pruning-logikken. Begge er binærdata i CAS med
|
||||
en TTL som forlenges ved aksess. Forskjellen er bare at generert
|
||||
innhold alltid kan gjenskapes fra kilden — så det er tryggere å prune.
|
||||
|
||||
### Workspace-styrt aggressivitet
|
||||
Hvert workspace kan justere sin pruning-profil:
|
||||
- **Konservativt** — behold alt lenge (f.eks. arkiv-workspace)
|
||||
- **Aggressivt** — tekst bevares, binærdata prunes raskt (f.eks.
|
||||
daglig drift-workspace med mye rutineinnhold)
|
||||
- **Tilpasset** — egne regler per modalitet og edge-type
|
||||
|
||||
### Brukerens erfaringsbaserte meny
|
||||
Over tid bruker du noen edges oftere enn andre, noen noder oftere enn
|
||||
andre. Maskinrommet observerer dette og tilbyr en erfaringsbasert meny:
|
||||
dine mest brukte koblinger, dine vanligste input-mønstre, dine
|
||||
foretrukne modaliteter. Ikke som en rigid konfigurasjon — som en
|
||||
adaptiv overflate du kan aktivere og deaktivere fortløpende.
|
||||
|
||||
Dette er ikke maskinlæring eller kompleks AI — det er frekvenstelling
|
||||
på edges og aksesslog. Enkelt å implementere, intuitivt for brukeren.
|
||||
|
||||
## Pragmatisk vei dit
|
||||
|
||||
Ikke bygg dette fra scratch. Formaliser det som allerede finnes:
|
||||
|
||||
1. **Worker + jobbkø er allerede kjernen.** De trenger et konsistent
|
||||
API, ikke en omskriving.
|
||||
2. **AI Gateway (LiteLLM) absorberes** — i stedet for en separat proxy,
|
||||
blir LLM-kall en operasjon i maskinrommet som alt annet.
|
||||
3. **Whisper, TTS, mediaprosessering** — allerede planlagt som
|
||||
worker-jobber. Gi dem samme grensesnitt.
|
||||
4. **LiveKit** — den mest spesielle tjenesten (sanntidsstrømmer). Kan
|
||||
starte som en separat integrasjon og formaliseres inn over tid.
|
||||
|
||||
Rekkefølge: definer traits → migrer eksisterende worker-jobber inn →
|
||||
legg til nye tjenester etter hvert. Fast punkt fra dag én, full
|
||||
dekning over tid.
|
||||
|
||||
## Spenninger og åpne spørsmål
|
||||
|
||||
- **Synkron vs asynkron.** "Fang" og "lever" kan være instant, men
|
||||
"prosesser" kan ta sekunder (TTS) eller minutter (full episode-
|
||||
transkripsjon). Grensesnittet må håndtere begge naturlig.
|
||||
- **Strømmer.** Live lyd/video er fundamentalt annerledes enn
|
||||
request/response. Men edge-modellen løser mye: maskinrommet ser en
|
||||
stream-edge og vet at det betyr LiveKit. Utfordringen er *reaktivitet*
|
||||
— maskinrommet må observere edge-endringer i sanntid og koble inn/ut
|
||||
ressurser dynamisk.
|
||||
- **Granularitet.** Hvor mye skal maskinrommet vite om domenet? "Fang
|
||||
lyd" er generisk, men "transkriber og splitt i segmenter med
|
||||
taler-identifikasjon" er domenespesifikt. Hvor går grensen?
|
||||
- **Overhead.** Et ekstra lag betyr et ekstra kall. For tunge
|
||||
operasjoner (Whisper, LLM) er det neglisjerbart. For lette
|
||||
operasjoner (slå opp metadata) kan det være unødvendig indirection.
|
||||
|
||||
## Plassering i lagmodellen
|
||||
|
||||
Maskinrommet (Rust) er det eneste orkestringslaget. Alle tjenester —
|
||||
inkludert PG, SpacetimeDB, CAS, Whisper, LiteLLM, LiveKit — er
|
||||
likeverdige tjenester *under* maskinrommet. SpacetimeDB er ikke et lag
|
||||
mellom Rust og GUI, det er en tjeneste maskinrommet skriver til.
|
||||
|
||||
Ett unntak: SpacetimeDB har en direkte WebSocket-kobling til frontend
|
||||
for sanntids lese-strøm. Dette er en bevisst optimering — STDB sitt
|
||||
klient-SDK gir ~10μs-oppdateringer med automatisk synk og lokal cache.
|
||||
Å proxy dette gjennom Rust ville vært å bygge en dårligere versjon av
|
||||
noe STDB gjør optimalt.
|
||||
|
||||
Se [datalaget](datalaget.md) for full lagmodell med diagram.
|
||||
|
||||
## Forhold til andre retninger
|
||||
|
||||
Maskinrommet er infrastrukturen *under* de tre primitivene i
|
||||
[universell input og mottak](universell_input.md):
|
||||
- Input-primitiven kaller `fang()` + `prosesser()`
|
||||
- Mottak-primitiven kaller `lever()`
|
||||
- Kommunikasjonsnoden bruker alle tre (fang input fra deltakere,
|
||||
prosesser sanntid, lever til mottakere)
|
||||
|
||||
Det er også det som gjør to-lags-modellen fra [rom, ikke forum](rom_ikke_forum.md)
|
||||
praktisk: maskinrommet ruter til riktig lag (sanntid vs tradisjonelt)
|
||||
uten at primitivene trenger å vite forskjellen.
|
||||
211
docs/retninger/rom_ikke_forum.md
Normal file
211
docs/retninger/rom_ikke_forum.md
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
# Rom, ikke forum
|
||||
|
||||
> Hva om Sidelinja ikke er en webapp med sanntidsfunksjoner, men en oppslukende
|
||||
> sanntidsopplevelse som tilfeldigvis leverer tradisjonell funksjonalitet?
|
||||
|
||||
## Observasjoner
|
||||
|
||||
Sidelinja har vokst organisk. Vi har bygget chat, kanban, kalender, notater,
|
||||
kunnskapsgraf — hver som sin feature, hver med sin spec. SpacetimeDB ble lagt til
|
||||
for sanntid, men arkitekturen er fortsatt "PostgreSQL-app med sanntidskrydder."
|
||||
|
||||
Resultatet:
|
||||
- **Forum-følelsen.** Ting er organisert i tråder, kort, lister. Brukeren
|
||||
navigerer mellom sider. Det føles som et tradisjonelt verktøy med litt polish.
|
||||
- **Databasespenning.** PG og SpacetimeDB har et komplisert eierforhold.
|
||||
SpacetimeDB-loven løser grensesnittet, men ikke det underliggende spørsmålet:
|
||||
hva *er* primæropplevelsen?
|
||||
- **Feature-fragmentering.** Chat, kanban, whiteboard, notater — hver lever i sin
|
||||
boks. "Universell overføring" og "meldingsboks" prøver å lime dem sammen, men
|
||||
utgangspunktet er fortsatt separate primitiver.
|
||||
|
||||
## Tesen
|
||||
|
||||
Snu gravitasjonen:
|
||||
|
||||
**I stedet for:** Tradisjonell webapp → bolt på sanntid der det trengs
|
||||
**Tenk:** Sanntidsrom er default → persistens er en egenskap ved ting som skjer
|
||||
|
||||
Forskjellen er subtil men fundamental:
|
||||
- Et "dokument som flere kan redigere" vs "et rom der folk er sammen og ting
|
||||
de gjør blir husket"
|
||||
- "Gå til kanban-brettet" vs "åpne kanban-laget i rommet du allerede er i"
|
||||
- "Send en melding i chat" vs "si noe i rommet"
|
||||
|
||||
## Hva ville vært annerledes?
|
||||
|
||||
### SpacetimeDB som verden, PG som arkiv
|
||||
SpacetimeDB er ikke en "sanntidskache foran PG" — det er verdenen brukerne
|
||||
lever i. PG er arkivet som husker hva som har skjedd.
|
||||
|
||||
Rollene blir klare og adskilte:
|
||||
- **SpacetimeDB** = sanntidslaget. Aktivt samarbeid, live interaksjon,
|
||||
ting som skjer *nå*.
|
||||
- **PostgreSQL** = arkivet. Alt som noensinne har skjedd. Søk, historikk,
|
||||
statistikk, revisjon.
|
||||
|
||||
Men viktig: sanntidslaget er *bare* et sanntidslag. Ikke alt trenger
|
||||
sanntid. En kunnskapsgraf-utforsker, et søk i gamle episoder, en
|
||||
statistikkside, en offentlig publisert artikkel — disse snakker rett med
|
||||
PG-arkivet som tradisjonelle nettsider. De trenger ikke gå gjennom
|
||||
SpacetimeDB. Begge lagene leser og skriver til det samme arkivet.
|
||||
|
||||
Dermed har vi to parallelle overflater:
|
||||
- **Sanntidsopplevelsen** (via SpacetimeDB) — for alt som er levende,
|
||||
aktivt, samarbeidende. Chat, whiteboard, live redigering.
|
||||
- **Tradisjonelt lag** (rett mot PG) — for alt som er retrospektivt,
|
||||
utforskende, statisk. Arkiv, søk, publisering, statistikk.
|
||||
|
||||
Dataflyt mellom dem: ting som oppstår i sanntidslaget synkes til PG.
|
||||
Ting i PG kan løftes inn i sanntidslaget når de blir aktive igjen.
|
||||
Men det er ingen eierskapskonflikt — de to lagene har fundamentalt
|
||||
forskjellige roller. Det er ikke to konkurrerende sannheter, det er
|
||||
*nåtid* og *arkiv*, med to overflater som passer til hver sin rolle.
|
||||
|
||||
### Rommet som primitiv, ikke siden
|
||||
I dag navigerer brukeren mellom `/chat`, `/kanban`, `/kalender`. I "rom"-modellen
|
||||
er brukeren alltid *et sted*, og funksjonalitet er lag som kan slås av og på:
|
||||
chat-laget, oppgave-laget, tidslinje-laget. Alt eksisterer i samme sanntidsrom.
|
||||
|
||||
### Tilstedeværelse som førsteklasses konsept
|
||||
Hvem er her nå? Hva ser de på? Hva jobber de med? I en tradisjonell webapp er
|
||||
dette en "feature" (online-indikator). I rom-modellen er det fundamentet alt
|
||||
annet bygger på.
|
||||
|
||||
### Interaksjon før organisering
|
||||
I dag: lag et kanban-kort → fyll ut feltene → flytt det. I rom-modellen: si noe,
|
||||
tegn noe, del noe → det som oppstår kan *bli* et kort, en oppgave, en notis —
|
||||
organisering skjer etterpå, ikke som forutsetning.
|
||||
|
||||
## Den administrative opplevelsen
|
||||
|
||||
Tesen ovenfor kan føles abstrakt. Mer konkret: Sidelinja bør være en
|
||||
**administrativ opplevelse** — ikke et spill med avatarer, men et arbeidsmiljø
|
||||
der produktive ting er lette å oppnå og strukturen følger deg, ikke omvendt.
|
||||
|
||||
### Formløs input, struktur etterpå
|
||||
I dag velger du visning først: "nå er jeg i chat", "nå er jeg i kanban." Men
|
||||
meldingsboksen er allerede designet for at input ikke trenger å vite hva den
|
||||
*er* på forhånd. En tanke bør kunne starte som en løs setning og bli et
|
||||
kanban-kort, en faktoid, en kalenderoppføring — uten at brukeren måtte
|
||||
bestemme det på forhånd. Visninger er flyktige linser. Innhold er permanent.
|
||||
|
||||
### Trylle frem, legge fra seg
|
||||
Trenger du et whiteboard? Det dukker opp. En videosamtale? Den starter.
|
||||
En dagbok? Den er der. Og når fokuset tar en annen retning legger du det
|
||||
fra deg uten at det blir borte — kunnskapsgrafen holder styr på det. Du
|
||||
trenger ikke "lukke" noe eller "lagre" noe. Ting eksisterer i verden
|
||||
og kan gjenfinnes.
|
||||
|
||||
### Siloer forsvinner
|
||||
"Universell overføring" som eksplisitt feature blir overflødig fordi det
|
||||
ikke finnes separate steder å overføre *mellom*. Du endrer ikke *hvor*
|
||||
noe er — du endrer *hvordan* du ser på det som allerede er der. Chat,
|
||||
kanban, kalender er ikke apper — de er visninger av samme tilstandsrom.
|
||||
|
||||
### Privat og delt som lag, ikke separate systemer
|
||||
"Personlig workspace" trenger ikke være en egen ting. Privat/delt er bare en
|
||||
synlighetsbryter på alt du gjør. Du chatter med en kollega og samtidig skriver
|
||||
du dagbok — begge er meldingsbokser, den ene er delt, den andre er privat.
|
||||
De eksisterer side om side i samme rom.
|
||||
|
||||
Dette åpner for naturlig flyt mellom kontekster: du sitter på bussen, snakker
|
||||
inn en tanke via voice, den transkriberes automatisk og lander som en privat
|
||||
meldingsboks — dagboknotis, oppgavepåminnelse, idé til neste episode. Du
|
||||
trenger ikke åpne "dagbok-appen" eller "notat-appen." Du bare snakker, og
|
||||
systemet tar vare på det riktig sted basert på kontekst og synlighet.
|
||||
|
||||
Input-metode (tekst, voice, tegning) og synlighet (privat, delt, publisert)
|
||||
er ortogonale egenskaper. Ingen av dem bør diktere *hva* innholdet blir.
|
||||
|
||||
### SpacetimeDB som naturlig motor
|
||||
SpacetimeDB tenker "dette eksisterer i verden nå" — ikke "lagre dette i
|
||||
riktig tabell." Å trylle frem et whiteboard er naturlig i en verden-modell:
|
||||
det er bare et nytt objekt med en tilstand. I PG-modellen må du opprette
|
||||
rader, definere relasjoner, sette opp persistens. SpacetimeDB låner seg
|
||||
til flytende, formløs interaksjon på en måte PG ikke gjør.
|
||||
|
||||
PG briljerer i rollen som arkiv: relasjonelle spørringer over historikk,
|
||||
fulltekstsøk, pgvector for semantisk søk, aggregeringer og statistikk.
|
||||
Når du spør "hva snakket vi om i mars?" er det PG som svarer. Når du
|
||||
spør "hva skjer nå?" er det SpacetimeDB.
|
||||
|
||||
## Spenninger og åpne spørsmål
|
||||
|
||||
- **Ytelse.** En alltid-på sanntidsopplevelse krever mer av både klient og
|
||||
server enn tradisjonelle sideinnlastinger. Er SpacetimeDB klar for dette?
|
||||
- **Kompleksitet.** "Alt er et rom" høres elegant ut, men kan bli kaotisk.
|
||||
Hvordan unngår vi at det blir uoversiktlig?
|
||||
- **Discovery vs fokus.** "Alt kan bli hva som helst" er kraftig, men kan
|
||||
bli overveldende. Et whiteboard du tryller frem må være like lett å finne
|
||||
igjen om tre uker. Kunnskapsgrafen er kanskje svaret — den er allerede
|
||||
designet for å koble ting uavhengig av type.
|
||||
- **~~Gradvis overgang.~~** *(Løst — se "Innebygd utviklingsstrategi" nedenfor.)*
|
||||
- **Solo-bruk.** Mye av verdien i "rom" kommer fra å være der sammen. Hvordan
|
||||
føles det for én person som jobber alene?
|
||||
- **Er det vi allerede har?** Meldingsboks-konseptet, universell overføring og
|
||||
SpacetimeDB-loven peker allerede i denne retningen. Kanskje dette ikke er en
|
||||
ny retning, men en artikulering av det vi ubevisst har bygget mot?
|
||||
- **Inspirasjon.** Spillverdener (MMO-lobbyer, shared spaces), Figma (alle i
|
||||
samme canvas), tldraw, Gather.town. Hva kan vi lære fra disse?
|
||||
|
||||
## Innebygd utviklingsstrategi
|
||||
|
||||
To-lags-modellen gir en viktig implikasjon for utviklingen: **innebygd fallback
|
||||
og to veier inn til alt.**
|
||||
|
||||
Ny funksjonalitet kan alltid starte i det tradisjonelle laget — rett mot PG,
|
||||
vanlig request/response, kjent terreng. Den fungerer med en gang. Når det
|
||||
senere gir verdi (samarbeid, sanntid, live interaksjon) kan den løftes inn
|
||||
i sanntidslaget. Men den trenger ikke det for å være nyttig.
|
||||
|
||||
Dette betyr:
|
||||
- **Ingenting vi har bygget er bortkastet.** Eksisterende PG-baserte features
|
||||
er ikke "gammel arkitektur" — de er det tradisjonelle laget, og det er et
|
||||
fullverdig lag.
|
||||
- **Ingen stor omskriving.** Retningen er ikke en migrasjon med en frist. Den
|
||||
er en utviklingsstrategi: bygg tradisjonelt, løft til sanntid ved behov.
|
||||
- **Risikoen er lav.** Hvis SpacetimeDB skuffer, har vi fortsatt et komplett
|
||||
tradisjonelt system. Hvis det leverer, får ting gradvis en rikere opplevelse.
|
||||
- **Primitivene er nøkkelen.** Så lenge meldingsboksen og kunnskapsgrafen er
|
||||
fleksible nok, kan begge lag bruke dem. Arkitekturen holder uavhengig av
|
||||
hvilket lag en feature lever i.
|
||||
|
||||
## Kritisk vurdering
|
||||
|
||||
### SpacetimeDB er en ung teknologi
|
||||
Å lene seg tungt på SpacetimeDB er et veddemål. Hvis prosjektet stagnerer,
|
||||
endrer API, eller har skaleringstak vi ikke ser ennå — er vi eksponert.
|
||||
*Mildnet* av at det tradisjonelle laget mot PG alltid finnes som fallback:
|
||||
SpacetimeDB er ikke alt-eller-ingenting, men et lag for det som trenger
|
||||
sanntid. Resten lever trygt mot PG uansett.
|
||||
|
||||
### "Formløs input" er vanskelig i praksis
|
||||
Det høres elegant ut, men noen må bestemme hva noe *blir*. AI? Brukeren
|
||||
etterpå? Automatiske regler? Notion, Roam Research og mange andre startet
|
||||
med lignende ambisjoner og endte opp med å gi brukeren eksplisitte
|
||||
strukturverktøy — fordi ambiguitet i praksis skaper friksjon, ikke flyt.
|
||||
Risikoen er at vi bygger noe som føles magisk i demoen og forvirrende i
|
||||
hverdagen.
|
||||
|
||||
### Grensen mellom "nåtid" og "arkiv" er uklar
|
||||
En samtale fra i går som fortsatt er relevant — er den "nå" eller "arkiv"?
|
||||
Et kanban-kort som har stått stille i to uker? Vi trenger regler for når ting
|
||||
flyttes mellom lagene, og de reglene kan bli like komplekse som
|
||||
SpacetimeDB-loven de erstatter. "Tid og arkiv" er renere *i prinsippet*,
|
||||
men i praksis er "aktiv" et spektrum, ikke en binær tilstand.
|
||||
|
||||
### Solo-bruk er underadressert
|
||||
Vegard er primærbruker. "Rom"-konseptet henter mye av sin kraft fra
|
||||
tilstedeværelse og samarbeid. For én person som produserer podcast er det
|
||||
en reell risiko at sanntidslaget er overhead sammenlignet med et godt
|
||||
organisert tradisjonelt verktøy. *Mildnet* av to-lags-modellen: solo-bruk
|
||||
kan lene seg tyngre på det tradisjonelle PG-laget, og sanntidslaget
|
||||
aktiveres når det faktisk gir verdi (live innspilling, samarbeid).
|
||||
|
||||
### Omfanget
|
||||
*Betydelig mildnet* av utviklingsstrategien ovenfor. Retningen krever ikke
|
||||
en omskriving — den er kompatibel med inkrementell utvikling. Den
|
||||
gjenværende risikoen er mer subtil: at vi bruker mental energi på å
|
||||
vurdere "bør dette være sanntid?" for hver feature i stedet for å bare
|
||||
bygge. Pragmatisk default bør være: bygg tradisjonelt, løft senere.
|
||||
72
docs/retninger/status_quo.md
Normal file
72
docs/retninger/status_quo.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Status quo — Hva Sidelinja er i dag
|
||||
|
||||
> En redaksjonell webapp med ambisiøse primitiver og tradisjonell overflate.
|
||||
|
||||
## Hva fungerer
|
||||
|
||||
### Meldingsboksen som universell primitiv
|
||||
Den viktigste arkitekturbeslutningen. Én datamodell — meldingsboksen — er
|
||||
underlag for chat, kanban-kort, kalenderoppføringer, notater og faktoider.
|
||||
I stedet for fem separate domenemodeller har vi én fleksibel primitiv med
|
||||
view-konfigurasjoner oppå. Dette er genuint uvanlig og gir oss muligheter
|
||||
de fleste redaksjonelle verktøy ikke har.
|
||||
|
||||
### Kunnskapsgrafen
|
||||
Nodes og edges i PostgreSQL gir en rik struktur for å koble alt med alt —
|
||||
personer, temaer, episoder, fakta. Dette er ryggraden i det redaksjonelle
|
||||
arbeidet og skiller Sidelinja fra enklere verktøy.
|
||||
|
||||
### Self-hosted med full kontroll
|
||||
Hetzner VPS, Caddy, Authentik, Forgejo. Ingen avhengighet til skytjenester
|
||||
vi ikke kontrollerer. For et journalistisk verktøy er dette ikke bare
|
||||
en preferanse — det er et prinsipp.
|
||||
|
||||
### AI som infrastruktur, ikke feature
|
||||
LiteLLM som gateway, BYOK-modell, Whisper for transkripsjon. AI er ikke
|
||||
en knapp i UI-et — det er en del av maskineriet. Jobbkø-arkitekturen gjør
|
||||
at tunge operasjoner aldri blokkerer brukeropplevelsen.
|
||||
|
||||
## Hva som er tradisjonelt
|
||||
|
||||
### Navigasjon
|
||||
Brukeren beveger seg mellom `/chat`, `/kanban`, `/kalender` som separate
|
||||
sider. Til tross for at datamodellen er universell, føles opplevelsen
|
||||
fragmentert — som et sett med separate verktøy som deler database.
|
||||
|
||||
### Interaksjonsmodell
|
||||
CRUD-mønsteret dominerer: opprett, rediger, slett, flytt. Interaksjonen
|
||||
er form-basert og eksplisitt. Brukeren "administrerer innhold" mer enn
|
||||
de "jobber sammen i et miljø."
|
||||
|
||||
### Sanntid som tillegg
|
||||
SpacetimeDB er lagt til for å gi sanntidsoppdatering, men arkitekturen
|
||||
er PostgreSQL-først. Sanntid er noe som *skjer med* tradisjonelle
|
||||
operasjoner, ikke noe som er *grunnlaget* for opplevelsen.
|
||||
|
||||
## Spenninger
|
||||
|
||||
### To sannhetskilder
|
||||
PG og SpacetimeDB har et komplisert forhold. SpacetimeDB-loven definerer
|
||||
klare regler for hvem som eier hva, men selve eksistensen av loven vitner
|
||||
om en arkitektonisk spenning: vi har to systemer som begge vil være
|
||||
primærkilde, og vi bruker konvensjoner for å holde dem fra å kollidere.
|
||||
|
||||
### Ambisiøs bunn, forsiktig topp
|
||||
Meldingsboksen og kunnskapsgrafen åpner for opplevelser vi ikke leverer
|
||||
ennå. Datamodellen sier "alt henger sammen" — men UI-et sier "her er
|
||||
chatten, her er kanban-brettet, her er kalenderen." Grunnmuren er mer
|
||||
spennende enn det brukeren ser.
|
||||
|
||||
### Produksjonsverktøy vs opplevelse
|
||||
Sidelinja er designet for podcast-produksjon, men pendler mellom å være
|
||||
et effektivt arbeidsverktøy og noe mer oppslukende. Studioet og
|
||||
møterommet peker mot sanntidsopplevelser. Redaksjonen og kanban peker
|
||||
mot tradisjonelt prosjektstyringsverktøy. Begge er gyldige, men de
|
||||
trekker i ulike retninger.
|
||||
|
||||
## Oppsummert
|
||||
|
||||
Sidelinja har en sterkere grunnmur enn overflaten viser. Meldingsboksen,
|
||||
kunnskapsgrafen og AI-infrastrukturen er genuint interessante primitiver.
|
||||
Spørsmålet er ikke om vi har bygget feil — men om overflaten utnytter
|
||||
det fundamentet faktisk tillater.
|
||||
299
docs/retninger/universell_input.md
Normal file
299
docs/retninger/universell_input.md
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
# 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
|
||||
20
ops/README.md
Normal file
20
ops/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Ops — Repeterbare vedlikeholdsjobber
|
||||
|
||||
Denne mappen inneholder veldefinerte, repeterbare jobber for å holde prosjektet
|
||||
ryddig og gjennomsiktig. Hver jobb er en markdown-fil med sjekkliste som kan
|
||||
kjøres av Claude eller manuelt.
|
||||
|
||||
## Jobber
|
||||
|
||||
| Jobb | Fil | Frekvens | Beskrivelse |
|
||||
|------|-----|----------|-------------|
|
||||
| Ryddejobb | [ryddejobb.md](ryddejobb.md) | Annenhver uke / ved behov | Full revisjon av prosjektet — docs, kode, drift, fremdrift |
|
||||
| Doc-audit | [doc-audit.md](doc-audit.md) | Månedlig / etter store endringer | Sjekk at docs/ stemmer med faktisk kode |
|
||||
| Drift-sjekk | [drift-sjekk.md](drift-sjekk.md) | Ved deploy / ved behov | Asynkron tilstand mellom prod, lokal og docs |
|
||||
|
||||
## Konvensjoner
|
||||
|
||||
- Hver jobb har seksjonene: **Hva**, **Når**, **Sjekkliste**, **Sist kjørt**
|
||||
- «Sist kjørt»-seksjonen oppdateres hver gang jobben kjøres
|
||||
- Funn skrives som korte bullet points med dato
|
||||
- Fikser gjøres underveis eller logges som oppgaver i `tasks/`
|
||||
46
ops/doc-audit.md
Normal file
46
ops/doc-audit.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Doc-audit — Dokumentasjon vs kode
|
||||
|
||||
## Hva
|
||||
Målrettet gjennomgang av `docs/`-treet for å sikre at dokumentasjonen stemmer med
|
||||
faktisk kode. Mer fokusert enn ryddejobben — kun docs.
|
||||
|
||||
## Når
|
||||
- Månedlig som rutine
|
||||
- Etter store refaktoreringer eller arkitekturendringer
|
||||
- Når nye docs legges til
|
||||
|
||||
## Sjekkliste
|
||||
|
||||
### 1. Filreferanser
|
||||
- [ ] Sjekk alle filstier nevnt i docs — eksisterer de?
|
||||
- [ ] Sjekk alle komponent-/modul-navn nevnt i docs — eksisterer de i koden?
|
||||
- [ ] Sjekk alle route-referanser — matcher de `web/src/routes/`?
|
||||
|
||||
### 2. CLAUDE.md doc-tre
|
||||
- [ ] Er alle filer i `docs/` listet i CLAUDE.md doc-treet?
|
||||
- [ ] Er det filer listet i CLAUDE.md som ikke eksisterer?
|
||||
- [ ] Er beskrivelsene i doc-treet fortsatt dekkende?
|
||||
|
||||
### 3. Tekniske detaljer
|
||||
- [ ] Stemmer database-skjema beskrevet i docs med faktiske migrasjoner?
|
||||
- [ ] Stemmer API-endepunkter beskrevet i docs med faktiske routes?
|
||||
- [ ] Stemmer SpacetimeDB-tabeller/reducere i docs med modulen?
|
||||
|
||||
### 4. Setup-docs
|
||||
- [ ] `docs/setup/lokal.md` — fungerer stegene fortsatt?
|
||||
- [ ] `docs/setup/produksjon.md` — stemmer med faktisk server-oppsett?
|
||||
- [ ] `docs/setup/migration_safety.md` — er sjekklisten oppdatert?
|
||||
|
||||
### 5. Proposals
|
||||
- [ ] Er noen proposals implementert og bør flyttes til `concepts/` eller `features/`?
|
||||
- [ ] Er noen proposals foreldet og bør slettes eller arkiveres?
|
||||
- [ ] Er noen proposals egentlig retningsspørsmål og bør flyttes til `retninger/`?
|
||||
|
||||
### 6. Retninger
|
||||
- [ ] Er tesene i `docs/retninger/` fortsatt relevante?
|
||||
- [ ] Har noen retninger modnet nok til å påvirke andre docs (arkitektur, features, infra)?
|
||||
- [ ] Er det nye arkitektoniske spenninger som fortjener en egen retning?
|
||||
|
||||
## Sist kjørt
|
||||
|
||||
_Ikke kjørt ennå._
|
||||
45
ops/drift-sjekk.md
Normal file
45
ops/drift-sjekk.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Drift-sjekk — Prod vs lokal vs docs
|
||||
|
||||
## Hva
|
||||
Verifiser at produksjonsserveren, lokalt utviklermiljø og dokumentasjon er i synk.
|
||||
Fanger opp tilfeller der noe er deployet men ikke dokumentert, eller dokumentert
|
||||
men ikke implementert.
|
||||
|
||||
## Når
|
||||
- Før og etter deploy til produksjon
|
||||
- Når noe oppfører seg annerledes i prod vs lokalt
|
||||
- Ved mistanke om drift
|
||||
|
||||
## Sjekkliste
|
||||
|
||||
### 1. Git-status
|
||||
- [ ] Er prod-server på siste commit? (`ssh sidelinja@157.180.81.26 'cd /srv/sidelinja/server && git log -1'`)
|
||||
- [ ] Er det ucommittede endringer lokalt som burde vært pushet?
|
||||
- [ ] Er det commits på Forgejo som ikke er deployet til prod?
|
||||
|
||||
### 2. Database-migrasjoner
|
||||
- [ ] Er alle lokale migrasjoner pushet til repo?
|
||||
- [ ] Er alle migrasjoner i repo kjørt i prod?
|
||||
- [ ] Stemmer migrasjonsnumre mellom miljøer?
|
||||
|
||||
### 3. Docker-tjenester
|
||||
- [ ] Kjører alle forventede containere i prod? (`docker compose ps`)
|
||||
- [ ] Er det tjenester i `docker-compose.dev.yml` som mangler i prod (eller omvendt)?
|
||||
- [ ] Er image-versjoner oppdatert?
|
||||
|
||||
### 4. Miljøvariabler
|
||||
- [ ] Er det nye env-vars lagt til lokalt som mangler i prod `.env`?
|
||||
- [ ] Er det env-vars i prod som er utdaterte?
|
||||
- [ ] Er secrets rotert der de bør være?
|
||||
|
||||
### 5. SpacetimeDB-modul
|
||||
- [ ] Er SpacetimeDB-modulen publisert med siste endringer?
|
||||
- [ ] Stemmer modul-skjema mellom lokal og prod?
|
||||
|
||||
### 6. Caddy / reverse proxy
|
||||
- [ ] Er Caddyfile i repo synk med prod?
|
||||
- [ ] Er det nye subdomener eller routes som mangler?
|
||||
|
||||
## Sist kjørt
|
||||
|
||||
_Ikke kjørt ennå._
|
||||
65
ops/ryddejobb.md
Normal file
65
ops/ryddejobb.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Ryddejobb — Full prosjektrevisjon
|
||||
|
||||
## Hva
|
||||
Systematisk gjennomgang av hele prosjektet for å oppdatere fremdrift, tette hull,
|
||||
fjerne utdaterte referanser, og sikre at dokumentasjon stemmer med virkeligheten.
|
||||
|
||||
## Når
|
||||
- Annenhver uke som rutine
|
||||
- Etter store implementeringsjobber
|
||||
- Når prosjektet føles uoversiktlig
|
||||
|
||||
## Sjekkliste
|
||||
|
||||
### 1. CLAUDE.md — stemmer instruksjonene?
|
||||
- [ ] Er stack-beskrivelsen oppdatert?
|
||||
- [ ] Er doc-treet komplett (alle filer i `docs/` er listet)?
|
||||
- [ ] Er reglene fortsatt relevante?
|
||||
- [ ] Finnes det nye konvensjoner som bør inn?
|
||||
|
||||
### 2. Docs vs virkelighet
|
||||
- [ ] Gå gjennom `docs/concepts/` — stemmer beskrivelsene med hva som finnes i koden?
|
||||
- [ ] Gå gjennom `docs/features/` — er det features beskrevet som ikke er påbegynt? Marker dem.
|
||||
- [ ] Gå gjennom `docs/infra/` — stemmer infrastruktur-docs med `docker-compose.dev.yml` og prod?
|
||||
- [ ] Gå gjennom `docs/setup/` — fungerer oppsettinstruksjonene fortsatt?
|
||||
- [ ] Gå gjennom `docs/retninger/` — er tesene fortsatt relevante? Har noen modnet til beslutninger?
|
||||
- [ ] Er det docs som refererer til filer, routes eller komponenter som ikke eksisterer?
|
||||
|
||||
### 3. Kode-hygiene
|
||||
- [ ] Ubrukte SvelteKit-routes (mapper i `web/src/routes/` uten innhold eller med stub)
|
||||
- [ ] Ubrukte komponenter (filer i `web/src/lib/components/` som ikke importeres)
|
||||
- [ ] Ubrukte Rust-moduler i worker
|
||||
- [ ] Ubrukte SpacetimeDB-reducere eller tabeller
|
||||
- [ ] Gamle migrations som bør dokumenteres eller konsolideres
|
||||
- [ ] `package.json` / `Cargo.toml` — ubrukte dependencies
|
||||
|
||||
### 4. Fremdriftsstatus
|
||||
- [ ] Hva er faktisk implementert og fungerer?
|
||||
- [ ] Hva er påbegynt men ufullstendig?
|
||||
- [ ] Hva er kun planlagt (kun docs)?
|
||||
- [ ] Oppdater en kort statusoversikt (kan legges i `ops/status.md` ved behov)
|
||||
|
||||
### 5. Asynkron tilstand — prod vs lokal vs docs
|
||||
- [ ] Stemmer `docker-compose.dev.yml` med det som faktisk kjøres lokalt?
|
||||
- [ ] Er prod-server oppdatert med siste push?
|
||||
- [ ] Er det migrasjoner som er kjørt lokalt men ikke i prod (eller omvendt)?
|
||||
- [ ] Er miljøvariabler (.env) synkronisert mellom miljøer?
|
||||
|
||||
### 6. CLAUDE.md minne
|
||||
- [ ] Gå gjennom `~/.claude/projects/-home-vegard-server/memory/MEMORY.md`
|
||||
- [ ] Fjern utdaterte minner
|
||||
- [ ] Oppdater minner som har blitt unøyaktige
|
||||
- [ ] Er det ny kunnskap fra nylige samtaler som bør lagres?
|
||||
|
||||
### 7. Erfaringslogg
|
||||
- [ ] Er det gjort arbeid nylig som mangler erfaringsdokumentasjon i `docs/erfaringer/`?
|
||||
- [ ] Er eksisterende erfaringsdokumenter fortsatt relevante og korrekte?
|
||||
|
||||
### 8. dev.sh og utviklermiljø
|
||||
- [ ] Fungerer `./dev.sh` fra scratch?
|
||||
- [ ] Er alle nødvendige tjenester dekket?
|
||||
- [ ] Er det nye quirks eller workarounds som bør inn i scriptet?
|
||||
|
||||
## Sist kjørt
|
||||
|
||||
_Ikke kjørt ennå._
|
||||
Loading…
Add table
Reference in a new issue