Compare commits
No commits in common. "6ec430141bc3c87263c55fc5702c1fb41fa34b3c" and "17b2c44bddbc6e5f063359b560915939961f4ddf" have entirely different histories.
6ec430141b
...
17b2c44bdd
14 changed files with 2 additions and 1463 deletions
|
|
@ -51,13 +51,6 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
||||||
- `synkronisering.md` — PostgreSQL ↔ SpacetimeDB dataflyt og eierskapsmodell
|
- `synkronisering.md` — PostgreSQL ↔ SpacetimeDB dataflyt og eierskapsmodell
|
||||||
- `api_grensesnitt.md` — Kommunikasjonskart: SvelteKit er web-API, Rust er worker
|
- `api_grensesnitt.md` — Kommunikasjonskart: SvelteKit er web-API, Rust er worker
|
||||||
- `ai_gateway.md` — LiteLLM som sentralisert AI-ruter (BYOK + OpenRouter fallback)
|
- `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/proposals/` — Halvtenkte idéer og kreative innfall (se `README.md` for oversikt)
|
||||||
- `docs/erfaringer/` — Lærdommer fra implementering (feller, anti-patterns, løsninger):
|
- `docs/erfaringer/` — Lærdommer fra implementering (feller, anti-patterns, løsninger):
|
||||||
- `svelte5_reaktivitet.md` — $state-getters, SSR-feller, polling-mønster
|
- `svelte5_reaktivitet.md` — $state-getters, SSR-feller, polling-mønster
|
||||||
|
|
@ -88,7 +81,7 @@ CLAUDE.md er eneste startdokument. Alt annet ligger under `docs/`:
|
||||||
- Tunge AI-jobber (Whisper, LLM-kall) skal aldri blokkere web-requests
|
- 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
|
- 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
|
- Kod og test lokalt i WSL2, deploy via push til Forgejo + SSH pull
|
||||||
- Sjekk alltid relevant doc i `docs/concepts/`, `docs/features/`, `docs/infra/` eller `docs/retninger/` før du implementerer
|
- Sjekk alltid relevant doc i `docs/concepts/`, `docs/features/` eller `docs/infra/` før du implementerer
|
||||||
- Sjekk `docs/erfaringer/` for kjente feller før du implementerer med Svelte 5, SpacetimeDB eller adapter-mønsteret
|
- 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/`
|
- Etter ferdig implementering av en komponent: dokumenter lærdommer i `docs/erfaringer/`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,11 @@ Halvtenkte idéer, kreative innfall og ting vi vil utforske når vi får tid. Ik
|
||||||
## Pipeline
|
## Pipeline
|
||||||
|
|
||||||
```
|
```
|
||||||
retninger/ → påvirker alt (arkitektoniske teser)
|
|
||||||
proposals/ → features/ eller concepts/
|
proposals/ → features/ eller concepts/
|
||||||
(idé) (spesifisert, klar for implementering)
|
(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. Idéer som er for store og fundamentale for proposals — arkitektoniske teser om prosjektets retning — hører hjemme i `docs/retninger/`.
|
Når en idé modnes nok til å bli implementert, skrives en full spec i `docs/features/` eller `docs/concepts/` og forslaget slettes herfra.
|
||||||
|
|
||||||
## Oversikt
|
## Oversikt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
# 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)
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
@ -1,301 +0,0 @@
|
||||||
# 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.
|
|
||||||
|
|
@ -1,211 +0,0 @@
|
||||||
# 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.
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
# 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.
|
|
||||||
|
|
@ -1,299 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# 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/`
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# 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å._
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
# 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å._
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
# 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å._
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Samler all prosjektdokumentasjon til én fil for deling med AI-er etc.
|
|
||||||
# Bruk: ./collect-docs.sh → skriver scripts/server_context.md
|
|
||||||
# ./collect-docs.sh - → skriver til stdout (for piping)
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
||||||
OUT="$SCRIPT_DIR/summary.md"
|
|
||||||
|
|
||||||
files=(
|
|
||||||
# Overblikk — visjon og retning først
|
|
||||||
"$ROOT/docs/arkitektur.md"
|
|
||||||
"$ROOT/CLAUDE.md"
|
|
||||||
"$ROOT"/docs/retninger/*.md
|
|
||||||
|
|
||||||
# Hva vi bygger
|
|
||||||
"$ROOT"/docs/concepts/*.md
|
|
||||||
"$ROOT"/docs/features/*.md
|
|
||||||
|
|
||||||
# Hvordan det henger sammen
|
|
||||||
"$ROOT"/docs/infra/*.md
|
|
||||||
|
|
||||||
# Idéer og utforskning
|
|
||||||
"$ROOT"/docs/proposals/*.md
|
|
||||||
|
|
||||||
# Lærdommer og drift
|
|
||||||
"$ROOT"/docs/erfaringer/*.md
|
|
||||||
"$ROOT"/docs/setup/*.md
|
|
||||||
"$ROOT"/ops/*.md
|
|
||||||
|
|
||||||
# Databaseskjema
|
|
||||||
"$ROOT"/migrations/*.sql
|
|
||||||
)
|
|
||||||
|
|
||||||
collect() {
|
|
||||||
for f in "${files[@]}"; do
|
|
||||||
[[ -f "$f" ]] || continue
|
|
||||||
rel="${f#"$ROOT/"}"
|
|
||||||
echo "================================================================"
|
|
||||||
echo "FILE: $rel"
|
|
||||||
echo "================================================================"
|
|
||||||
echo ""
|
|
||||||
cat "$f"
|
|
||||||
echo ""
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ "${1:-}" == "-" ]]; then
|
|
||||||
collect
|
|
||||||
else
|
|
||||||
collect > "$OUT"
|
|
||||||
echo "Wrote $OUT ($(wc -l < "$OUT") lines)"
|
|
||||||
fi
|
|
||||||
Loading…
Add table
Reference in a new issue