From 5b6876093edcb9866aa6133acc6d7b8b44bdbcae Mon Sep 17 00:00:00 2001 From: vegard Date: Thu, 19 Mar 2026 17:29:50 +0000 Subject: [PATCH] Oppgaver som noder: erstatter tasks.md med graf-basert modell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fjernet fil-basert oppgaveliste (tasks.md, tasks-arkiv.md, tasks/). Oppgaver er nå noder (node_kind: 'task') med prioritet, status, agent-tildeling og krasj-deteksjon via PG. Atomisk plukking med FOR UPDATE SKIP LOCKED. Dokumentert i docs/infra/oppgaver.md. Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 9 +- docs/infra/oppgaver.md | 110 ++++++++++ tasks-arkiv.md | 461 ----------------------------------------- tasks.md | 76 ------- 4 files changed, 114 insertions(+), 542 deletions(-) create mode 100644 docs/infra/oppgaver.md delete mode 100644 tasks-arkiv.md delete mode 100644 tasks.md diff --git a/CLAUDE.md b/CLAUDE.md index 000735a..4538c07 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,15 +25,13 @@ plattformkode og infrastruktur er skilt fra tenant-data og -innhold. til disk underveis (f.eks. `docs/fikseliste.md` eller en dedikert fil i `docs/`). Kontekst kan forsvinne ved planmodus-reset eller krasj. Neste instans skal kunne lese seg opp fra filene og fortsette. -- **Oppgaveliste:** `tasks.md` er den aktive arbeidslisten. Kun åpne - oppgaver. Når en oppgave er ferdig: flytt til `tasks-arkiv.md` med - dato, fjern fra `tasks.md`. Listen skal alltid være ren. +- **Oppgaver:** Oppgaver er noder i grafen (`node_kind: 'task'`). + Plukkes av agenter via PG-query, ikke filer. Se `docs/infra/oppgaver.md`. ## Dokumentasjonstre CLAUDE.md er eneste startdokument. Oppgavelister i roten, docs under `docs/`: -- `tasks.md` — Aktiv arbeidsliste (kun åpne oppgaver) -- `tasks-arkiv.md` — Fullførte oppgaver med dato (fase 1-30 + løpende) +- `docs/infra/oppgaver.md` — Oppgaver som noder i grafen - `docs/arkitektur.md` — Overordnet arkitektur, lagmodell, teknologivalg - `docs/retninger/` — Arkitektoniske teser og vedtatte retninger: @@ -75,6 +73,7 @@ CLAUDE.md er eneste startdokument. Oppgavelister i roten, docs under `docs/`: - `synkronisering.md` — Historisk: PG ↔ SpacetimeDB dataflyt (arkivdokument, SpacetimeDB fjernet mars 2026) - `claude_agent.md` — Claude som chat-deltaker: arkitektur, triggere, sikkerhet - `observerbarhet.md` — Strukturert logging, metrikk-endepunkt (/metrics), AI-kostnad + - `oppgaver.md` — Oppgaver som noder: prioritet, status, agent-plukking, krasj-deteksjon - `docs/erfaringer/` — Lærdommer fra v1 (Svelte 5, Authentik). STDB-docs arkivert i `arkiv/` - `reference/` — Kode fra v1 med gjenbruksverdi (Editor.svelte) - `ops/` — Repeterbare vedlikeholdsjobber (ryddejobb, doc-audit, drift-sjekk) diff --git a/docs/infra/oppgaver.md b/docs/infra/oppgaver.md new file mode 100644 index 0000000..2c5bb0a --- /dev/null +++ b/docs/infra/oppgaver.md @@ -0,0 +1,110 @@ +# Oppgaver som noder + +Oppgaver er noder i grafen. Ingen separat tabell, ingen filer. +Samme primitiver som alt annet i Synops. + +## Node-type + +``` +node_kind: 'task' +title: "Fiks resize på mobil" +content: "Beskrivelse, akseptkriterier, kontekst..." +visibility: 'hidden' +metadata: { + priority: 100, // lavere = viktigere + status: "open", // open | active | done | blocked | question + category: "ui", // ui | platform | infra | feature | bug + agent_id: null, // node_id til agenten som jobber på den + started_at: null, // timestamp + completed_at: null, // timestamp + result: null, // "success" | "failed" | "escalated" + refs: ["docs/proposals/mine_ting.md"] // referanser +} +``` + +## Edges + +``` +task --[created_by]--> person (hvem opprettet) +task --[assigned_to]--> agent/person (hvem jobber på den) +task --[belongs_to]--> collection (tilhører et prosjekt, valgfritt) +task --[related_to]--> task (avhengigheter mellom oppgaver) +``` + +## Prioriteter + +| Verdi | Kategori | +|-------|----------| +| 100 | UI-forbedringer (pågående arbeid) | +| 200 | Høy prioritet (grunnleggende plattform) | +| 300 | Middels prioritet (produksjon og samarbeid) | +| 400 | Infrastruktur | +| 500 | Lavthengende frukt | +| 600 | Fremtidige features | + +## Arbeidsflyt + +### Opprette oppgaver + +- Vegard i vaktmester-chat: `/task Fiks resize på mobil` +- Admin-panel: oppgaveliste med "ny oppgave"-knapp +- Epost: `vaktmester@synops.no` med `/task ...` +- Programmatisk: `synops-agent` eller maskinrommet + +### Agent plukker oppgave + +```sql +SELECT * FROM nodes +WHERE node_kind = 'task' +AND metadata->>'status' = 'open' +ORDER BY (metadata->>'priority')::int ASC +LIMIT 1 +FOR UPDATE SKIP LOCKED +``` + +`FOR UPDATE SKIP LOCKED` gir atomisk plukking — flere agenter +kan kjøre parallelt uten konflikter. + +### Agent jobber + +1. Sett `status: "active"`, `agent_id`, `started_at` +2. Les `content` og `refs` for kontekst +3. Utfør oppgaven (implementer, skriv, commit) +4. Sett `status: "done"`, `completed_at`, `result` + +### Krasj-deteksjon + +```sql +SELECT * FROM nodes +WHERE node_kind = 'task' +AND metadata->>'status' = 'active' +AND (metadata->>'started_at')::timestamptz < now() - interval '60 minutes' +``` + +Oppgaver som har stått "active" i >60 min uten oppdatering +→ sett tilbake til `status: "open"`, nullstill `agent_id`. + +## Visning + +### Admin-panel + +Oppgaveliste med: +- Filtrer på status, kategori, prioritet +- Drag-and-drop for prioritetsendring +- Klikk for å åpne/redigere +- Vis hvem som jobber på hva + +### Mine ting + +Oppgaver du er tildelt vises under "Mine oppgaver". + +### Vaktmester-chat + +Vegard kan spørre: "Hva jobbes det med nå?" → agent lister +aktive oppgaver med status. + +## Migrering fra tasks.md + +Eksisterende oppgaver fra den gamle tasks.md-listen bør +opprettes som task-noder. Kan gjøres manuelt (admin-panel) +eller via et migreringsscript. diff --git a/tasks-arkiv.md b/tasks-arkiv.md deleted file mode 100644 index 9864c7c..0000000 --- a/tasks-arkiv.md +++ /dev/null @@ -1,461 +0,0 @@ -# Synops — Oppgavearkiv - -Fullførte oppgaver med timestamp. Aktiv arbeidsliste: `tasks.md`. - ---- - -## 2026-03-19: UI-forbedringer (domene/ruting/workspace) - -- [x] Domene/ruting/auth-opprydding (ws.synops.no) -- [x] Mottak fjernet, arbeidsflaten er startpunkt -- [x] AI-verktøy og Ressursforbruk som BlockShell-paneler -- [x] Unifisert ContextHeader (innstillinger, tema, kontekst-velger) -- [x] Fargevelger med per-element kontroll og presets -- [x] Panel-resize fungerer (alle kanter og hjørner) -- [x] Canvas: pan med klikk-dra, kameraposisjon lagres, zoom ±1% -- [x] Z-order: sist berørt boks øverst -- [x] Paneler husker brukerens foretrukne størrelse -- [x] Egendefinerte tema-slots (lagre/gjenbruk) -- [x] Toolbar fungerer, dobbeltklikk = tilpass visning -- [x] Gruppe-drag med lasso -- [x] Grid-innstilling lagres - ---- - -## Fase 1-30: Opprinnelig implementering - -Sekvensiell liste. Hver oppgave gjort i én Claude Code-sesjon. - -## Statuser - -- `- [ ]` — Klar til å gjøres -- `- [~]` — Pågår. En agent jobber på denne. Andre agenter hopper over. -- `- [x]` — Ferdig -- `- [?]` — Åpent spørsmål, trenger avklaring fra Vegard. -- `- [!]` — Blokkert av teknisk problem. - -`[~]`, `[?]` og `[!]` blokkerer alle oppgaver som avhenger av denne. -Detaljer skrives som innrykket tekst med `>` prefix under oppgaven. -Runner-scriptet legger automatisk til `> Påbegynt: ` for `[~]`. - -Hvis en `[~]`-oppgave har stått i >60 min uten commit, anta at -sesjonen krasjet. Kjør `run-next-task.sh --unstale` for å frigjøre. - -## Avhengigheter - -Oppgaver innen en fase er sekvensielle (1.1 før 1.2, osv). -Faser avhenger av hverandre slik: - -``` -Fase 1 (infra) → Fase 2 (maskinrommet) → Fase 3 (frontend) - ↘ Fase 4 (tilgang) -Fase 3 + 4 → Fase 5 (kommunikasjon) -Fase 2 → Fase 6 (CAS) → Fase 7 (lyd) -Fase 5 → Fase 8 (aliaser) -Fase 3 → Fase 9 (visninger) -Fase 2 → Fase 10 (AI) -Fase 5 + 6 + 7 → Fase 11 (produksjon) -Fase 3 + 4 → Fase 13 (traits) -Fase 6 + 13 → Fase 14 (publisering) -Fase 3 + 10 → Fase 15 (adminpanel) -Fase 11 + 13 → Fase 16 (lydmixer) -Fase 17 (lydstudio-utbedring) — ingen avhengigheter, kan kjøres parallelt -Fase 10 + 13 → Fase 18 (AI-verktøy) -Fase 3 + 13 → Fase 19 (arbeidsflaten — spatial canvas) -Fase 19 → Fase 20 (universell overføring + panelrework) -Fase 2 → Fase 21 (CLI-verktøy — unix-filosofi) -Alt → Fase 12 (herding) -Fase 12 → Fase 22 (SpacetimeDB-migrering — fullført) -Fase 22 → Fase 23 (validering — alle faser) -Fase 23 → Fase 24 (orkestrering) -Fase 24 → Fase 25 (web clipper) -Fase 25 → Fase 26 (epost) -Fase 25 → Fase 27 (tankekart) — uavhengig av epost -Fase 25 → Fase 28 (manglende CLI + AI-ruting) — uavhengig av epost -Fase 25 → Fase 29 (universell input) — uavhengig av epost -Fase 25 → Fase 30 (podcast-hosting) — uavhengig av epost -``` - -Hvis en oppgave er `[?]` eller `[!]`, hoppes den over — og alle -oppgaver som avhenger av den (innen fasen + avhengige faser). -Uavhengige faser kan fortsatt plukkes. - ---- - -## Fase 1: Infrastruktur-fundament - -- [x] 1.1 PostgreSQL schema: opprett database `synops`, enums (`visibility`, `access_level`), tabeller (`nodes`, `edges`, `node_access`, `auth_identities`) med indekser. Kjør på server via SSH. Ref: `docs/primitiver/nodes.md`, `docs/primitiver/edges.md`, `docs/retninger/bruker_ikke_workspace.md`. -- [x] 1.2 Seed-data: opprett Vegards brukernode (`node_kind='person'`, `title='Vegard'`) og `auth_identities`-rad. Opprett Sidelinja samlings-node og `owner`-edge fra Vegard. -- [x] 1.3 SpacetimeDB modul: opprett Rust-modul med `nodes` og `edges`-tabeller som speiler PG-skjema. Grunnleggende reducers for CRUD. Deploy til server. Ref: `docs/retninger/datalaget.md`, `docs/erfaringer/spacetimedb_integrasjon.md`. -- [x] 1.4 Caddy-config: reverse proxy for maskinrommet (api.sidelinja.org), SpacetimeDB, og SvelteKit. Auto-TLS. Ref: `docs/setup/produksjon.md`. -- [x] 1.5 Authentik: opprett OIDC-provider og applikasjon for Synops. Konfigurer redirect URIs. Ref: `docs/erfaringer/authentik_oppsett.md`. - -## Fase 2: Maskinrommet — skjelett - -- [x] 2.1 Rust-prosjekt: opprett `maskinrommet/` med axum, tokio, sqlx (PG), serde. Dockerfile. Kompilerer og starter. Ref: `docs/retninger/maskinrommet.md`. -- [x] 2.2 Auth-middleware: valider Authentik JWT-tokens, slå opp `auth_identities` → node_id. Returner 401 for ugyldige tokens. -- [x] 2.3 SpacetimeDB-klient i maskinrommet: koble til STDB, skriv noder og edges via reducers. -- [x] 2.4 Skrivestien: `POST /intentions/create_node` — valider, skriv STDB (instant), spawn async PG-skriving. Returner node_id umiddelbart. -- [x] 2.5 Flere intensjoner: `create_edge`, `update_node`, `delete_node`. Validering av tilgang (created_by eller owner/admin-edge). -- [x] 2.6 Docker Compose: legg maskinrommet inn i server-stacken. Intern nettverkstilgang til PG og STDB. - -## Fase 3: Frontend — skjelett - -- [x] 3.1 SvelteKit-prosjekt: opprett `frontend/` med TypeScript, TailwindCSS. PWA-manifest. Lokal dev med HMR. -- [x] 3.2 Authentik login: OIDC-flow (authorization code + PKCE). Session-håndtering. Redirect til login ved 401. -- [x] 3.3 STDB WebSocket-klient: abonner på noder og edges. Reaktiv Svelte-store som oppdateres ved endringer. -- [x] 3.4 Mottaksflaten v0: vis noder med edge til innlogget bruker, sortert på `created_at`. Enkel liste med tittel og utdrag. -- [x] 3.5 TipTap-editor: enkel preset (tekst, markdown, lenker). Send `create_node`-intensjon til maskinrommet ved submit. -- [x] 3.6 Sanntidstest: åpne to faner, skriv i én, se noden dukke opp i den andre via STDB. - -## Fase 4: Tilgangskontroll - -- [x] 4.1 `recompute_access` i maskinrommet: ved edge-endring, oppdater `node_access`-matrisen. Håndter direkte edges (owner, admin, member, reader). -- [x] 4.2 Team-transitivitet: member_of-edge til team → arv tilgang fra teamets edges. -- [x] 4.3 Visibility-filtrering: STDB-spørringer respekterer visibility-enum. Frontend ser bare noder brukeren har tilgang til. -- [x] 4.4 RLS-policies på PG: `node_access`-basert filtrering for tunge spørringer. - -## Fase 5: Kommunikasjonsnoder - -- [x] 5.1 Opprett kommunikasjonsnode: intensjon `create_communication` → node med `node_kind='communication'`, deltaker-edges, metadata (started_at). -- [x] 5.2 Kontekst-arv: input i kommunikasjonsnode → automatisk `belongs_to`-edge. -- [x] 5.3 Chat-visning i frontend: noder med `belongs_to`-edge til kommunikasjonsnode, sortert på tid, sanntid via STDB. -- [x] 5.4 Én-til-én chat: opprett kommunikasjonsnode med to deltakere. Full loop: skriv melding → vis i sanntid hos begge. - -## Fase 6: CAS og mediefiler - -- [x] 6.1 CAS-lagring: filsystem med content-addressable hashing (SHA-256). Katalogstruktur med hash-prefix. Deduplisering. -- [x] 6.2 Upload-endepunkt: `POST /intentions/upload_media` → hash fil, lagre i CAS, opprett media-node med `has_media`-edge. -- [x] 6.3 Serving: `GET /cas/{hash}` → stream fil fra disk. Caddy kan serve direkte for ytelse. -- [x] 6.4 Bilder i TipTap: drag-and-drop/paste → upload → CAS-node → inline i `metadata.document` via `node_id`. - -## Fase 7: Lyd-pipeline - -- [x] 7.1 faster-whisper oppsett: Docker-container, GPU hvis tilgjengelig, norsk modell. Ref: `docs/erfaringer/`. -- [x] 7.2 Transkripsjons-pipeline: lydfil i CAS → maskinrommet trigger Whisper → resultat i `content`-feltet. -- [x] 7.3 Voice memo i frontend: opptak-knapp i input-komponenten → upload → CAS → transkripsjon. -- [x] 7.4 Lyd-avspilling: spiller av original lyd fra CAS-node. Waveform-visning. -- [x] 7.5 Segmenttabell-migrasjon: opprett `transcription_segments`-tabell i PG. Oppdater `transcribe.rs` til SRT-format → parse → skriv segmenter. Miljøvariabler: `WHISPER_MODEL` (default "medium"), `WHISPER_INITIAL_PROMPT`. Ref: `docs/concepts/podcastfabrikken.md` § 3. -- [x] 7.6 Transkripsjonsvisning i frontend: segmenter med tidsstempler, avspillingsknapp per segment (hopper til riktig sted i lydfilen), redigerbare tekstfelt (setter `edited = true`). Universell komponent for podcast, møter, voice memos. -- [x] 7.7 Re-transkripsjonsflyt: ved ny transkripsjon, vis side-om-side med forrige versjon. Highlight manuelt redigerte segmenter fra forrige versjon. Bruker velger per segment. -- [x] 7.8 SRT-eksport: generer nedlastbar SRT-fil fra `transcription_segments`-tabellen. - -## Fase 8: Aliaser - -- [x] 8.1 Alias-noder: opprett alias-node med `alias`-edge (system=true) fra hovednoden. Usynlig for traversering. -- [x] 8.2 Kontekstbasert identitet: maskinrommet setter `created_by` til alias-node når brukeren opererer i kontekst der aliaset er vert/deltaker. - -## Fase 9: Flere visninger - -- [x] 9.1 Kanban-visning: noder med board-edge, gruppert på status-edge. Drag-and-drop for statusendring. -- [x] 9.2 Kalender-visning: noder med `scheduled`-edge, på tidslinje. -- [x] 9.3 Dagbok-visning: private noder (ingen delte edges), sortert på tid. -- [x] 9.4 Kunnskapsgraf: topic-noder, `mentions`-edges. Visuell graf-visning. - -## Fase 10: AI og beriking - -- [x] 10.1 LiteLLM oppsett: Docker-container, API-nøkler, modell-routing. Ref: `docs/infra/ai_gateway.md`. -- [x] 10.2 AI-foreslåtte edges: maskinrommet sender innhold til LLM → foreslår mentions, topics. -- [x] 10.3 Oppsummering: kommunikasjonsnode → AI-generert sammendrag som ny node. -- [x] 10.4 TTS: tekst → lyd via ElevenLabs. Mottaker-preferanse i metadata. - -## Fase 11: Produksjons-pipeline - -- [x] 11.1 LiveKit oppsett: Docker-container for WebRTC. Ref: `docs/setup/produksjon.md`. -- [x] 11.2 Sanntidslyd: kommunikasjonsnode med live-status → LiveKit-rom for deltakere. -- [x] 11.3 Pruning-logikk: TTL per modalitet, signaler som forlenger levetid, disk-nødventil. -- [x] 11.4 Podcast-RSS: samlings-node med publiserings-edges → generert RSS-feed. - -## Fase 13: Trait-system - -- [x] 13.1 Trait-metadata på samlingsnoder: maskinrommet validerer `metadata.traits`-objektet ved `create_node` og `update_node` for samlingsnoder. Avvis ukjente trait-navn. Ref: `docs/primitiver/traits.md`. -- [x] 13.2 Trait-aware frontend: samlingssider leser `traits` fra metadata og rendrer kun aktive komponenter. Dynamisk komponent-lasting basert på trait-liste. -- [x] 13.3 Pakkevelger: UI for å opprette ny samling med forhåndsdefinert pakke (nettmagasin, podcaststudio, redaksjon osv.) eller manuelt valg av traits. -- [x] 13.4 Trait-administrasjon: admin-UI for å legge til/fjerne traits på eksisterende samlinger med konfigurasjon per trait. - -## Fase 14: Publisering - -- [x] 14.1 Tera-templates: innebygde temaer (avis, magasin, blogg, tidsskrift) med Tera i Rust. Artikkelmal + forside-mal per tema. CSS-variabler for theme_config-overstyring. Ref: `docs/concepts/publisering.md` § "Temaer". -- [x] 14.2 HTML-rendering av enkeltartikler: maskinrommet rendrer `metadata.document` til HTML via Tera, lagrer i CAS. Noden får `metadata.rendered.html_hash` + `renderer_version`. SEO-metadata (OG-tags, canonical, JSON-LD). -- [x] 14.3 Forside-rendering: maskinrommet spør PG for hero/featured/strøm (tre indekserte spørringer), appliserer tema-template, rendrer til CAS (statisk modus) eller serverer med in-memory cache (dynamisk modus). `index_mode` og `index_cache_ttl` i trait-konfig. -- [x] 14.4 Caddy-ruting for synops.no/pub: Caddy reverse-proxyer til maskinrommet som gjør slug→hash-oppslag og streamer CAS-fil. `Cache-Control: immutable` for artikler. Kategori/arkiv/søk serveres dynamisk av maskinrommet med kortere cache-TTL. -- [x] 14.5 Slot-håndtering i maskinrommet: `slot` og `slot_order` i `belongs_to`-edge metadata. Ved ny hero → gammel hero flyttes til strøm. Ved featured over `featured_max` → FIFO tilbake til strøm. `pinned`-flagg forhindrer automatisk fjerning. -- [x] 14.6 Forside-admin i frontend: visuell editor for hero/featured/strøm. Drag-and-drop mellom plasser. Pin-knapp. Forhåndsvisning. Oppdaterer edge-metadata via maskinrommet. -- [x] 14.7 Publiseringsflyt i frontend (personlig): publiseringsknapp på noder i samlinger med `publishing`-trait der `require_approval: false`. Forhåndsvisning, slug-editor, bekreftelse. Avpublisering ved fjerning av edge. -- [x] 14.8 RSS/Atom-feed: samling med `rss`-trait genererer feed automatisk ved publisering/avpublisering. `synops.no/pub/{slug}/feed.xml`. Maks `rss_max_items` (default 50). -- [x] 14.9 Custom domains: bruker registrerer domene i `publishing`-trait. Maskinrommet validerer DNS, Caddy on-demand TLS med validerings-callback. Re-rendring med riktig canonical URL. -- [x] 14.10 Redaksjonell innsending: `submitted_to`-edge med status-metadata (`pending`, `in_review`, `revision_requested`, `rejected`, `approved`). Maskinrommet validerer at kun roller i `submission_roles` kan opprette `submitted_to`, og kun owner/admin kan endre status eller opprette `belongs_to`. Ref: `docs/concepts/publisering.md` § "Innsending". -- [x] 14.11 Redaktørens arbeidsflate: frontend-visning av noder med `submitted_to`-edge til samling, gruppert på status. Kanban-stil drag-and-drop for statusendring. Siste kolonne ("Planlagt") setter `publish_at` i edge-metadata. -- [x] 14.12 Planlagt publisering: maskinrommet sjekker periodisk (cron/intervall) for `belongs_to`-edges med `publish_at` i fortiden som ikke er rendret. Ved treff: render HTML → CAS → oppdater RSS. -- [x] 14.13 Redaksjonell samtale: ved innsending kan redaktør opprette kommunikasjonsnode knyttet til artikkel + forfatter for diskusjon/feedback utover kort notat i edge-metadata. -- [x] 14.14 Bulk re-rendering: batch-jobb via jobbkø ved temaendring. Paginert (100 artikler om gangen), oppdaterer `renderer_version`. Artikler serveres med gammelt tema til re-rendret. -- [x] 14.15 Dynamiske sider: kategori-sider (filtrert på tag-edges), arkiv (kronologisk med månedsgruppering), søk (PG fulltekst). Alle paginerte, cachet i maskinrommet. Om-side som statisk CAS-node. -- [x] 14.16 Presentasjonselementer som noder: publisert tittel, ingress, OG-bilde, undertittel er egne noder med `title`/`summary`/`og_image`-edges til artikkelen. Frontend for å opprette/redigere varianter. Ref: `docs/concepts/publisering.md` § "Presentasjonselementer". -- [x] 14.17 A/B-testing: maskinrommet roterer varianter ved forside-rendering, logger impressions/klikk per variant, normaliserer CTR mot tidspunkt-baseline. Etter statistisk signifikans markeres vinner. Redaktør kan overstyre. Edge-metadata: `ab_status`, `impressions`, `clicks`, `ctr`. - -## Fase 15: Adminpanel - -- [x] 15.1 Systemvarsler: varslingsnode (`node_kind='system_announcement'`) med type (info/warning/critical), nedtelling og utløp. Frontend viser banner/toast for alle aktive klienter via STDB. Ref: `docs/concepts/adminpanelet.md`. -- [x] 15.2 Graceful shutdown: admin setter vedlikeholdstidspunkt → nedtelling i frontend → nye LiveKit-rom blokkeres → jobbkø stopper → vent på aktive jobber → restart. Vis aktive sesjoner før bekreftelse. -- [x] 15.3 Jobbkø-oversikt: admin-UI for aktive, ventende og feilede jobber. Filtrer på type/samling/status. Manuell retry og avbryt. -- [x] 15.4 AI Gateway-konfigurasjon: admin-UI for modelloversikt, API-nøkler (kryptert), ruting-regler per jobbtype, fallback-kjeder, forbruksoversikt per samling. Ref: `docs/infra/ai_gateway.md`. -- [x] 15.5 Ressursstyring: prioritetsregler mellom jobbtyper, ressursgrenser per worker, ressurs-governor for automatisk nedprioritering under aktive LiveKit-sesjoner, disk-status med varsling. -- [x] 15.6 Serverhelse-dashboard: tjeneste-status (PG, STDB, Caddy, Authentik, LiteLLM, Whisper, LiveKit), metrikker (CPU, minne, disk), backup-status, logg-tilgang. -- [x] 15.7 Ressursforbruk-logging: `resource_usage_log`-tabell i PG. Maskinrommet logger AI-tokens (inn/ut, modellnivå), Whisper-tid (sek), TTS-tegn, CAS-lagring (bytes), LiveKit-tid (deltaker-min). Båndbredde via Caddy-logg-parsing. Ref: `docs/features/ressursforbruk.md`. -- [x] 15.8 Forbruksoversikt i admin: aggregert visning per samling, per ressurstype, per tidsperiode. Drill-down til jobbtype og modellnivå. -- [x] 15.9 Brukersynlig forbruk: hver bruker ser eget forbruk i profil/innstillinger. Per-node forbruk synlig i node-detaljer for eiere. - -## Fase 16: Lydmixer - -Ref: `docs/features/lydmixer.md` - -- [x] 16.1 LiveKit-klient i frontend: installer `livekit-client`, koble til rom, vis deltakerliste. Deaktiver LiveKit sin auto-attach av `