Commit graph

287 commits

Author SHA1 Message Date
8e80102f6b Fullfør oppgave 22.2: Frontend-migrering fra SpacetimeDB til portvokteren
Frontend bruker nå kun portvokterens WebSocket for sanntidsdata.
SpacetimeDB-klienten er erstattet med en enkel WebSocket-klient
som kobler til /ws-endepunktet og oppdaterer reactive stores direkte.

Frontend-endringer:
- Nye lokale typer (types.ts) erstatter STDB module_bindings
- connection.svelte.ts: WebSocket til portvokteren med auto-reconnect
- stores.svelte.ts: Prosesserer WS-meldinger (initial_sync + events)
- MixerTrait: STDB-reducers erstattet med HTTP API-kall
- api.ts: Nye mixer-endepunkter (create, gain, mute, effect, role)
- +layout.svelte: Fjernet dual-tilkobling, kun portvokterens WS
- pg-ws.svelte.ts: Slettet (erstattet av connection.svelte.ts)

Dokumentasjon:
- datalaget.md: Fase M1+M2 markert som fullført
- api_grensesnitt.md: Oppdatert arkitekturdiagram, nye mixer-endepunkter
2026-03-18 12:26:33 +00:00
ce1c06d794 Legg til fase 23: validering og kvalitetssikring per fase
Ny fase med 11 oppgaver som spinner opp friske sesjoner for å
teste og validere implementeringen fra hver fase. Ved funn:
fiks direkte (småting) eller opprett nye tasks med spesifikasjon
for dedikert sesjon. Dekker alle faser fra infra til STDB-migrering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:10:38 +00:00
b5611706f9 Starter oppgave 22.2 2026-03-18 12:05:21 +00:00
d7a9f3816d Fullfør oppgave 22.1: WebSocket-lag med PG LISTEN/NOTIFY og frontend dual-tilkobling
Backend:
- ws.rs: Fikset WS auth via query-param (browser kan ikke sende headers ved WS upgrade)
- auth.rs: Gjort decoding_key pub for gjenbruk i ws-modulen

Frontend:
- pg-ws.svelte.ts: Ny PG WebSocket-klient med auto-reconnect og event-logging
- index.ts: Eksporterer pgWsConnect/pgWsDisconnect/pgWsState
- +layout.svelte: Kobler til PG WS i parallell med STDB for verifisering

Docs:
- api_grensesnitt.md: Dokumentert /ws endepunkt og sanntidsarkitektur
- tasks.md: Merket 22.1 som ferdig

Deploy: Krever restart av maskinrommet + rebuild av frontend.
2026-03-18 12:01:10 +00:00
6ee50e937d Starter oppgave 22.1 2026-03-18 11:47:22 +00:00
b31ee59868 Ytelse: profiler PG-spørringer, optimaliser node_access-oppdatering (oppgave 12.4)
Profilert alle kritiske PG-spørringer med EXPLAIN ANALYZE.
Identifiserte at recompute_access brukte single-column index
(idx_edges_type) med lav selektivitet, og RLS-policyer manglet
composite indexes for effektive oppslag.

Endringer:

Migrasjon 017_query_performance.sql:
- 6 nye composite indexes:
  - idx_edges_target_type (target_id, edge_type) — recompute_access + belongs_to
  - idx_edges_source_type (source_id, edge_type) — alias-oppslag
  - idx_edges_target_memberof (partial, member_of) — team-propagering
  - idx_nodes_created_at_desc — ORDER BY created_at DESC
  - idx_nodes_kind_created — filtrer på kind + sorter
  - idx_na_subject_covering INCLUDE (object_id) — RLS without heap lookup
- Optimalisert recompute_access(): steg 3 og 4 kjøres nå bare
  når det er relevant (EXISTS-sjekk først). For vanlige brukere
  (ikke team) unngår dette to fulle INSERT-SELECT-operasjoner.
- via_edge oppdateres nå korrekt ved access-nivå-endring.

Slow query logging (maskinrommet):
- Forespørsler >200ms logges som WARN med tag slow_request
- PG-spørringer >100ms logges som WARN med tag slow_query
- recompute_access-kall logges med varighet for overvåking
- Nytt pg_stats-felt i /metrics med tabell- og index-statistikk,
  cache hit ratio, og node_access-telling

Dokumentasjon oppdatert i docs/infra/observerbarhet.md.
2026-03-18 11:43:19 +00:00
7f8b749443 Starter oppgave 12.4 2026-03-18 11:30:21 +00:00
0fc559a207 Feilhåndtering: retry med backoff + dead letter queue for PG-skrivinger (oppgave 12.3)
Erstatter fire-and-forget tokio::spawn() i skrivestien med jobbkø-basert
persistering. Alle PG-skriveoperasjoner (insert/update/delete for noder
og edges) går nå gjennom den eksisterende jobbkøen som allerede har:

- Eksponentiell backoff (30s × 2^n) ved feil
- Dead letter queue (status='error' etter max_attempts=3)
- Admin-API for overvåking, manuell retry og avbryt
- Ressursstyring og prioritetsregler

Ny modul pg_writes.rs med:
- 5 enqueue-funksjoner (erstatter spawn_pg_*)
- 5 job-handlere for dispatch i worker-loopen
- Full paritet med gammel logikk: tilgangsgivende edges kjører
  recompute_access i transaksjon, synker til STDB, trigger rendering

Før: PG-skrivefeil logget og glemt → data kun i STDB, tapt fra PG.
Nå: automatisk retry → admin-synlig dead letter → manuell recovery.
2026-03-18 11:26:48 +00:00
9cd6c0ee5d Starter oppgave 12.3 2026-03-18 11:16:10 +00:00
81d2ece2f1 Revider datalaget: fas ut SpacetimeDB, PG LISTEN/NOTIFY + WebSocket
SpacetimeDB var et godt eksperiment, men gir synk-kompleksitet,
dobbelt vedlikehold og en ekstra SPOF for minimal latensgevinst
på denne skalaen. PG LISTEN/NOTIFY + WebSocket i portvokteren
erstatter sanntidslaget.

- datalaget.md: revidert med ny lagmodell, PG-triggers, migrasjonsplan
- retninger/README.md: oppdatert status til «Revidert»
- tasks.md: ny fase 22 (STDB-migrering) med 5 oppgaver (M1–M5)
- 12.4 oppdatert fra «profiler STDB» til «profiler PG»

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:15:46 +00:00
0f03886091 Backup: daglig PG-dump, STDB-krasj-recovery, helsesjekk (oppgave 12.2)
Tre ting implementert:

1. PG-dump rutine (scripts/backup-pg.sh):
   - Daglig cron kl. 03:00 UTC via /etc/cron.d/synops-backup
   - pg_dump -Fc (custom format, komprimert), konsistent uten nedetid
   - Rotasjon: beholder 30 dager, sletter eldre
   - Verifiserer at dump-filen er gyldig (ikke tom)

2. STDB → PG gjenoppbygging ved krasj (stdb_monitor.rs):
   - Bakgrunnsmonitor sjekker STDB hvert 30. sekund
   - Oppdager krasj (var oppe → nå nede)
   - Venter på at containeren restarter (maks 10 min)
   - Kjører warmup (PG → STDB) automatisk
   - Hele prosessen logges

3. Forbedret backup-helsesjekk (health.rs):
   - Sjekker /srv/synops/backup/pg/ for nyeste dump
   - Rapporterer ok/stale/missing i /admin/health
2026-03-18 11:11:32 +00:00
b68b9683dd Starter oppgave 12.2 2026-03-18 11:05:23 +00:00
0012a10373 Observerbarhet: strukturert logging, metrikker, /metrics-endepunkt (oppgave 12.1)
Legger til observerbarhetslaget i maskinrommet:

- Strukturert JSON-logging via LOG_FORMAT=json (maskinlesbart for
  log-aggregering). Default er human-readable for utvikling.
- Ny metrics-modul med in-memory request latency tracking per rute
  (count, avg, min, max, p50/p95/p99 fra siste 1000 forespørsler).
- Custom axum-middleware erstatter tower_http::TraceLayer — logger
  method, path, status og duration_ms per request, og mater
  metrikk-samleren.
- GET /metrics-endepunkt som returnerer:
  - request_latency: per-rute statistikk
  - queue_depth: pending/running/error/retry fra job_queue
  - ai_cost: aggregert fra ai_usage_log (siste time/24h/30d)
- Default loggnivå endret fra debug til info for mindre støy.
2026-03-18 11:01:36 +00:00
91a80d49a6 Starter oppgave 12.1 2026-03-18 10:55:31 +00:00
6496434bd3 synops-common: delt lib for alle CLI-verktøy (oppgave 21.16)
Ny crate `tools/synops-common` samler duplisert kode som var
spredt over 13 CLI-verktøy:

- db::connect() — PG-pool fra DATABASE_URL (erstatter 10+ identiske blokker)
- cas::path() — CAS-stioppslag med to-nivå hash-katalog
- cas::root() — CAS_ROOT env med default
- cas::hash_bytes() / hash_file() / store() — SHA-256 hashing og lagring
- cas::mime_to_extension() — MIME → filendelse
- logging::init() — tracing til stderr med env-filter
- types::{NodeRow, EdgeRow, NodeSummary} — delte FromRow-structs

Alle verktøy (unntatt synops-tasks som ikke bruker DB) er refaktorert
til å bruke synops-common. Alle kompilerer og tester passerer.
2026-03-18 10:51:40 +00:00
cee97c105f Starter oppgave 21.16 2026-03-18 10:40:20 +00:00
bfc88b9a80 Jobbkø-dispatcher: spawn CLI-verktøy i stedet for inline-kode (oppgave 21.15)
Alle jobbkø-handlere i maskinrommet delegerer nå til CLI-verktøy
(Command::new("synops-X")) i stedet for å kjøre logikk inline.
Stdout → jobbresultat (JSON), stderr → feillogg, exitkode → status.

Konverterte handlere:
- tts_generate → synops-tts (beholder voice-oppslag + STDB-synk)
- suggest_edges → synops-suggest-edges (STDB-synk utelatt, topics er bakgrunnsdata)
- render_article → synops-render --render-type article
- render_index → synops-render --render-type index
- audio_process → synops-audio (beholder STDB-synk)

Allerede CLI-delegerende (uendret):
- whisper_transcribe → synops-transcribe
- agent_respond → synops-respond
- summarize_communication → synops-summarize

Fortsatt inline (mangler CLI-verktøy):
- ai_process — planlagt i fremtidig oppgave

Ny felles modul: cli_dispatch.rs med run_cli_tool() og env-helpers.
synops-tts modifisert til å inkludere media_node_id i output.
Ref: docs/retninger/unix_filosofi.md
2026-03-18 10:36:38 +00:00
dac20102f5 Starter oppgave 21.15 2026-03-18 10:25:33 +00:00
23c63c2458 Implementer synops-node CLI-verktøy (oppgave 21.14)
Nytt CLI-verktøy for å hente og vise en node med alle tilkoblede edges.
Støtter rekursiv graf-traversering (--depth) og to output-formater
(markdown og JSON). Brukes av Claude og maskinrommet for å inspisere
graf-tilstand.

Features:
- Hent node med alle edges (inn og ut)
- Berik edges med peer-tittel og node_kind for lesbarhet
- --depth 0: bare noden, --depth 1: + edges (default), --depth 2+: traverser
- --format md (default) eller json
- Kompakt metadata-visning, forkortet innhold

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:24:43 +00:00
42c3876d67 Starter oppgave 21.14 2026-03-18 10:20:03 +00:00
9fd8fc6e57 Implementer synops-feature-status CLI-verktøy (oppgave 21.13)
Nytt verktøy som samler feature-status fra fire kilder:
1. Spec-sammendrag fra docs/features/ og docs/concepts/
2. Relaterte oppgaver fra tasks.md (fuzzy-matching på nøkkel)
3. Nylige git-commits (fil-endringer + commit-meldinger)
4. Ubesvart feedback fra PG (spec-noder med discusses-edge)

DATABASE_URL er valgfri — feedback hoppes over uten tilkobling.
Prøver også /tmp/maskinrommet.env som fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:18:43 +00:00
51b4a9aecf Starter oppgave 21.13 2026-03-18 10:12:57 +00:00
da2155e34e Implementer synops-tasks CLI-verktøy (oppgave 21.12)
Parser tasks.md og viser formatert oppgavestatus med filtrering
på fase (--phase N) og status (--status todo|done|blocked|inprogress|question).
Viser sammendrag med antall per status og fremdriftsbar.

Rent filbasert verktøy — ingen database nødvendig. Finner tasks.md
automatisk fra arbeidsdir eller absolutt sti.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:11:46 +00:00
590afabc23 Starter oppgave 21.12 2026-03-18 10:10:02 +00:00
cfec20f024 Implementer synops-search CLI-verktøy (oppgave 21.11)
Fulltekstsøk i noder via PostgreSQL tsvector/tsquery med norsk
tekstkonfigurasjon. Bruker eksisterende GIN-indeks fra migrasjon 011.
Støtter --kind-filter og --limit. Output: markdown med relevans-score
og utdrag (ts_headline med «»-markering).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:08:51 +00:00
61a0e186be Starter oppgave 21.11 2026-03-18 10:05:09 +00:00
b0faa6cf60 Implementer synops-context CLI-verktøy (oppgave 21.10)
Nytt verktøy som samler kontekst for en kommunikasjonsnode i
markdown-format — deltakere, spesifikasjon (discusses-edge),
meldingshistorikk og relaterte noder. Brukes av synops-respond
og andre verktøy som trenger samtalekontekst.

Input: --communication-id <uuid> [--max-messages N]
Output: markdown til stdout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:03:56 +00:00
304b4a62fa Starter oppgave 21.10 2026-03-18 09:59:19 +00:00
45efeb080a Implementer synops-prune CLI-verktøy (oppgave 21.9)
Erstatter maskinrommet/src/pruning.rs med selvstendig CLI-verktøy
som følger unix-filosofien. Tre-fase pruning:

- Fase 1 (disk ≥85%): Slett regenererbart innhold (TTS, thumbnails)
- Fase 2 (alltid): TTL-basert pruning per modalitet (lyd 30d, bilde 30d, video 14d)
- Fase 3 (disk ≥95%): Kritisk — alt uten publishing-edge slettes

Dry-run er default (krever --write for faktisk sletting).
JSON-output til stdout, logging til stderr.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:58:03 +00:00
f34dee810b Starter oppgave 21.9 2026-03-18 09:53:08 +00:00
b6cd2b4571 Implementer synops-respond CLI-verktøy (oppgave 21.8)
Bryter ut prosesseringslogikken fra maskinrommet/src/agent.rs til
et selvstendig CLI-verktøy: synops-respond. Følger unix-filosofien
der maskinrommet orkestrerer og CLI-verktøy gjør jobben.

Ansvarsdeling:
- maskinrommet beholder: kill switch, rate limiting, loop-prevensjon,
  STDB-skriving (sanntidsvisning for frontend)
- synops-respond håndterer: kontekst-henting fra PG, prompt-bygging,
  claude CLI-kall med retry, PG-skriving (node, edges, logging)

agent.rs er nå en tynn dispatcher (~140 linjer, ned fra ~305) som
validerer sikkerhet og spawner synops-respond, likt mønsteret fra
summarize.rs sin delegering til synops-summarize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:51:50 +00:00
595ac1a4d9 Starter oppgave 21.8 2026-03-18 09:44:17 +00:00
9032ab0225 Implementer synops-suggest-edges CLI-verktøy (oppgave 21.7)
Erstatter maskinrommet/src/ai_edges.rs med et selvstendig CLI-verktøy
som følger unix-filosofien: maskinrommet orkestrerer, verktøy gjør jobben.

Verktøyet analyserer en nodes innhold via LiteLLM og foreslår
topics og mentions (entiteter) som edges. I lesemodus (default)
returneres kun forslag som JSON med confidence-score. Med --write
opprettes topic-noder og mentions-edges i PG.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:43:03 +00:00
2ab73715f7 Starter oppgave 21.7 2026-03-18 09:38:09 +00:00
08ff14028b Implementer synops-summarize CLI-verktøy (oppgave 21.6)
Ekstraherer AI-oppsummeringslogikk fra maskinrommet til standalone
CLI-verktøy, i tråd med unix_filosofi.md-prinsippet om at maskinrommet
orkestrerer og CLI-verktøy gjør jobben.

synops-summarize:
- Henter meldinger og deltakere fra kommunikasjonsnode i PG
- Sender samtalelogg til LiteLLM for oppsummering
- Med --write: oppretter sammendrag-node, belongs_to/summary-edges,
  logger AI-ressursforbruk
- Uten --write: dry-run som skriver JSON til stdout

maskinrommet/src/summarize.rs er nå en tynn dispatcher som spawner
synops-summarize med --write, tilsvarende transcribe.rs-mønsteret.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:36:51 +00:00
8baeb0ec0e Starter oppgave 21.6 2026-03-18 09:30:24 +00:00
1dcecc2a04 Implementer synops-tts CLI-verktøy (oppgave 21.5)
Nytt CLI-verktøy som erstatter maskinrommet/src/tts.rs.
Kaller ElevenLabs API, lagrer generert lyd i CAS, og returnerer
CAS-hash som JSON. Med --write opprettes media-node og has_media-edge
i PostgreSQL, inkludert ressurslogging.

Følger samme mønster som synops-transcribe: clap-args, tracing til
stderr, JSON til stdout, atomisk CAS-lagring via temp+rename.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:29:12 +00:00
8034181e63 Starter oppgave 21.5 2026-03-18 09:25:12 +00:00
b766f063b8 Implementer synops-rss CLI-verktøy (oppgave 21.4)
Frittstående RSS/Atom-feed generator som erstatter maskinrommet/src/rss.rs.
Følger unix-filosofien: ett verktøy per oppgave, XML til stdout.

Støtter:
- Oppslag via --collection-id (UUID) eller --slug
- RSS 2.0 og Atom 1.0 (konfigurerbart via trait-metadata eller --format)
- Podcast-enclosures via has_media-edges
- --max-items for å begrense antall elementer

Verifisert mot prod-database med Sidelinja-samlingen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:24:01 +00:00
d4b016dc10 Starter oppgave 21.4 2026-03-18 09:20:17 +00:00
17e35b2644 Implementer synops-render CLI-verktøy (oppgave 21.3)
Nytt CLI-verktøy som rendrer artikler og forsider til HTML via
Tera-templates og lagrer resultatet i CAS. Erstatter rendering-logikken
i maskinrommet/src/publishing.rs som standalone verktøy.

Støtter to render-typer:
- article: Rendrer enkeltartikkel med SEO-metadata, presentasjonselementer,
  TipTap→HTML-konvertering, og tema-basert CSS.
- index: Rendrer forside med hero/featured/stream-artikler.

Fire innebygde temaer: avis, magasin, blogg, tidsskrift.
Templates er kopiert fra maskinrommet og innebygd via include_str!().
TipTap-modulen er duplisert inntil synops-common (21.16) samler felles kode.

Følger eksisterende CLI-mønster: --write gater DB-oppdateringer,
JSON til stdout, stderr for logging.

16 enhetstester dekker CSS-variabler, SEO, kategorisering, rendering
og TipTap-konvertering. Verifisert mot produksjons-DB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:19:02 +00:00
46fe19b78d Starter oppgave 21.3 2026-03-18 09:10:22 +00:00
34b086d046 Implementer synops-audio CLI-verktøy (oppgave 21.2)
Erstatter maskinrommet/src/audio.rs med frittstående CLI-verktøy
som følger unix_filosofi-mønsteret fra synops-transcribe.

Input: --cas-hash <hash> --edl <json>
Output: JSON med ny CAS-hash til stdout

Inkluderer:
- Fullstendig parametervalidering (NaN/Inf, grenseverdier)
- FFmpeg filtergraf i riktig rekkefølge (cuts→NR→EQ→comp→norm→fades)
- To-pass loudnorm for høykvalitets normalisering
- Silence detection → cut-konvertering med 200ms margin
- Atomisk CAS-lagring via temp-fil + rename
- --write flagg for DB-operasjoner (node + edge + ressurslogg)
- 21 enhetstester for validering, filtergraf og CAS-stier

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:09:02 +00:00
8fc3237033 Starter oppgave 21.2 2026-03-18 09:02:27 +00:00
bd12bed77e Implementer synops-transcribe CLI-verktøy (oppgave 21.1)
Bryter ut Whisper-transkribering fra maskinrommet til selvstendig
CLI-verktøy i tools/synops-transcribe/, i tråd med unix-filosofien.

Verktøyet:
- Leser lydfil fra CAS, sender til faster-whisper API (SRT-format)
- Parser SRT til segmenter, skriver JSON til stdout
- Med --write: skriver segmenter til PG, oppdaterer node metadata,
  logger ressursforbruk
- Støtter --cas-hash, --model, --initial-prompt, --language, --mime,
  --node-id, --requested-by

Maskinrommet sin transcribe.rs er nå en tynn dispatcher som spawner
synops-transcribe som subprosess med riktige env-variabler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:01:06 +00:00
cdd82e135d Starter oppgave 21.1 2026-03-18 08:53:51 +00:00
5babad9e59 Gjør StudioTrait til fullverdig BlockShell-panel med lydstudio-funksjonalitet (oppgave 20.9)
StudioTrait var en minimal liste med TraitPanel-wrapper. Nå er den et
fullverdig BlockShell-panel som følger mønsteret fra CalendarTrait (20.7)
og EditorTrait (20.8):

- Fjernet TraitPanel-wrapper, bruker egen flex-layout som fyller BlockShell
- Viser lydfiler med metadata: format-tag, varighet, størrelse, dato
- Drag-and-drop ut: lydfiler kan dras til andre paneler (editor, kalender)
- Drop-aksept: mottar lydfiler fra andre paneler via BlockReceiver
- Viser prosesserte versjoner (derived_from edges) per fil
- Responsivt med @container queries (360px, 500px) og @media fallback
- Lagt til `studio` trait i trait-katalogen i docs/primitiver/traits.md
- accessToken sendes nå fra collection page til StudioTrait

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:52:41 +00:00
204d46866f Starter oppgave 20.9 2026-03-18 08:48:41 +00:00
82dd61549b Gjør EditorTrait til fullverdig BlockShell-panel med inline editor og kilder (oppgave 20.8)
Fjerner TraitPanel-wrapper og erstatter med selvstendig panel-layout
som matcher KanbanTrait/CalendarTrait-mønsteret:

- Selvstendig layout som fyller BlockShell content area
- Listevisning med klikk-for-å-redigere (inline tittel + innhold)
- Source_material-edges vises som "Kilder"-sidebar i editor-visningen
- Visuell venstrekant-markering på artikler med kilder
- Opprett ny artikkel direkte fra panelet
- Container queries for responsiv tilpasning (small/medium/mobile)
- Beholder all eksisterende funksjonalitet: AI preset drops, publisering,
  drag-and-drop ut av panelet

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:47:20 +00:00
d8f635230e Starter oppgave 20.8 2026-03-18 08:41:55 +00:00