Commit graph

83 commits

Author SHA1 Message Date
eb2628c6a1 Auto Highlight Reel: AI-kuratert klipp-pakke fra podcast-transkripsjon
Ny feature: highlight_extract-jobb som analyserer fullstendig
transkripsjon etter innspilling og finner 5-10 klippverdige øyeblikk
(humor, emosjon, sterke meninger, punchlines, narrative høydepunkter).

Komponenter:
- synops-highlight CLI: henter segmenter, kaller AI, oppretter klipp-noder
- maskinrommet/highlight.rs: jobbdispatcher med modellrouting
- Registrert i jobbkø-dispatcher som "highlight_extract"

Hvert klipp blir en content-node med metadata (tidsstempler, score,
foreslått teksting, thumbnail-sitat, hashtags) og derived_from-edge
til episoden. Bruker synops/high-modell via AI Gateway.

Ref: docs/proposals/auto_highlight_reel.md
2026-03-19 21:40:50 +00:00
63b188641e Implementer brukeradministrasjon i admin (/admin/users)
- Backend: users_admin.rs med liste, toggle aktiv/deaktiv, AI-budsjett
- Frontend: brukeroversikt med roller, budsjett, siste aktivitet, filter
- API: fetchUsersOverview, toggleUser, updateUserBudget
2026-03-19 19:19:10 +00:00
a50245d0ac Implementer agent-oversikt i admin (/admin/agents)
Ny admin-side som viser registrerte AI-agenter med status, token-forbruk,
aktive jobber og kjørehistorikk. Støtter kill switch for å aktivere/deaktivere.
2026-03-19 19:13:04 +00:00
d53304a0f3 Implementer API-nøkkelhåndtering med kryptert lagring
- PG-migrasjon: api_keys-tabell med krypterte nøkler (032)
- AES-256-GCM kryptering via SYNOPS_MASTER_KEY (crypto.rs)
- Admin-endepunkter: list/create/test/deactivate/delete
- Test-tilkobling for OpenRouter, Anthropic, OpenAI, Gemini
- Frontend: /admin/keys med nøkkelliste og opprettskjema
- SYNOPS_MASTER_KEY injiseres via maskinrommet-env.sh
2026-03-19 18:57:01 +00:00
69997c6e87 Rydd sidelinja-referanser: generiske AI-nivåer + synops.no-domener
AI-aliaser: sidelinja/rutine → synops/low, sidelinja/resonering →
synops/high. Fire nivåer i LiteLLM: low/medium/high/extreme.
Oppdatert i: LiteLLM config, PG ai_job_routing, all Rust-kode
(maskinrommet + 5 CLI-verktøy).

Domener: sidelinja.org → synops.no i fallback-URLer, health-sjekker,
LiveKit WSS, bandwidth-logger, docs/erfaringer, docs/setup,
reference/server-state, .env.example.

Docker container-navn (sidelinja-*) beholdes — styrt av
COMPOSE_PROJECT_NAME i /srv/synops/.env, endres separat ved behov.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:22:22 +00:00
6370b02cc7 26.7 ferdig: utgående varsler med brukerpreferanser
Vaktmesteren kan nå sende epost-varsler og WebSocket-push til brukere
via synops-notify, med respekt for brukerens preferanser.

Endringer:
- jobs.rs: send_notification jobbtype som delegerer til synops-notify CLI
- synops-notify: preferansesjekk fra metadata.preferences.notifications
  (opt-out-modell, per-kanal og per-type bryter, --skip-preferences)
- intentions.rs: POST /intentions/send_notification (admin-only)
- Dokumentasjon: docs/features/varsler.md

Preferanseskjema (i brukernodens metadata):
  preferences.notifications.email: bool (global epost-bryter)
  preferences.notifications.ws: bool (global WS-bryter)
  preferences.notifications.<type>: bool (per-type, f.eks. task_assigned)
2026-03-19 02:08:00 +00:00
62b1ecd0b6 Podcast import wizard: backend + frontend (oppgave 30.7)
Backend (maskinrommet):
- Nytt modul podcast_import.rs med 4 endepunkter:
  POST /admin/podcast/import-preview (dry-run via CLI)
  POST /admin/podcast/import (starter jobb i køen)
  GET /admin/podcast/import-status (poll jobbstatus)
  GET /admin/podcast/collections (samlinger med podcast-trait)
- Ny jobbtype import_podcast i jobs.rs dispatcher

Frontend:
- Ny wizard-side /admin/podcast-import med 5 steg:
  1. RSS-URL + samling → forhåndsvisning
  2. Import (spinner med jobbstatus-polling)
  3. Resultat med sammenligning av feeds
  4. Re-import for nye episoder
  5. 301-redirect-info
- API-funksjoner i api.ts
- Navigasjonslenke i admin-panelet
2026-03-19 00:19:24 +00:00
4c1c470ed7 Embed podcast-spiller: /pub/{slug}/{episode}/player (oppgave 30.5)
Ny maskinrommet-handler som serverer en selvstående HTML-side med
podcast-spiller, designet for iframe-embedding på eksterne nettsider.

Spilleren inkluderer:
- Artwork (episode-spesifikk med fallback til samlingens)
- Tittel og podcast-navn
- Play/pause med loading-spinner
- WaveSurfer.js waveform-visualisering (CDN)
- Tidsvisning (nåværende/total)
- Kapittelmerkering (visuelt på waveform + klikkbar liste)
- Responsiv design (mobil-vennlig ned til 360px)
- Iframe-vennlige headers (X-Frame-Options, CSP frame-ancestors)

Rute: GET /pub/{slug}/{episode_id}/player
Registrert før {article_id} catch-all i rutehierarkiet.
2026-03-18 23:53:21 +00:00
3e57adce46 Podcast-statistikk dashboard i admin-panelet (oppgave 30.4)
Nytt dashboard under /admin/podcast-stats som viser:
- Nøkkeltall: totale nedlastinger, unike lyttere, antall episoder
- Daglig trend med horisontale bar charts
- Topp-episoder rangert etter nedlastinger
- Klientfordeling (Apple Podcasts, Spotify, etc.) med stacked bar

Backend: GET /admin/podcast/stats spør podcast_download_stats-tabellen
(fylt av synops-stats CLI fra oppgave 30.3) og aggregerer per episode,
per dag, og per klient via jsonb_each_text.

Filtrering på tidsperiode (7/30/90/365 dager) og enkelt-episode.
2026-03-18 23:42:23 +00:00
5b3367e7e5 CalDAV-abonnement: periodisk polling av eksterne kalendere (oppgave 29.12)
Utvider synops-calendar CLI med --url for å hente ICS fra eksterne URLer
(Google Calendar, Outlook, etc). Ny calendar_poller i maskinrommet poller
samlingers calendar_subscriptions[] med konfigurerbart intervall, etter
samme mønster som feed_poller for RSS-feeds.

Endringer:
- synops-calendar: ny --url parameter + reqwest for HTTP-henting
- calendar_poller.rs: bakgrunnsloop som finner forfalne abonnementer
- calendar_poll jobbtype i dispatcher med CLI-dispatch til synops-calendar
- API: configure_calendar_subscription + remove_calendar_subscription
- Migrasjon 031: indeks + prioritetsregel for calendar_poll-jobber
2026-03-18 23:04:29 +00:00
a3dfa3b254 Webhook-templates: forhåndsdefinerte mappinger for kjente tjenester (oppgave 29.6)
Legger til et template-system for webhooks som vet hvordan kjente
tjenester strukturerer sine JSON-payloads, og mapper dem til
meningsfulle node title/content/metadata.

Templates:
- github-push: Commits med repo, branch, pusher, formaterte meldinger
- github-issues: Issue-hendelser med nummer, labels, state
- github-pull-request: PR-hendelser med branch-info, merge-status
- slack-message: Slack Event API-meldinger med kanal og bruker
- ci-build: Generisk CI/CD (GitHub Actions, GitLab CI, Jenkins)

Backend:
- webhook_templates.rs: Template-definisjoner og apply-logikk
- webhook.rs: Bruker template fra webhook-nodens metadata.template_id
- webhook_admin.rs: GET /admin/webhooks/templates, POST set_template,
  template_id i create og list

Frontend:
- Template-velger i opprett-skjema og på hver webhook-kort
- Kan bytte template på eksisterende webhooks

6 unit-tester for alle templates. Verifisert med curl mot live endpoint.
2026-03-18 22:10:33 +00:00
097ef02aea Webhook-admin: UI for å opprette/administrere webhooks (oppgave 29.5)
Backend (maskinrommet):
- GET /admin/webhooks — liste alle webhooks med aktivitetsinfo
- GET /admin/webhooks/events?webhook_id=... — siste hendelser
- POST /admin/webhooks/create — opprett webhook for samling
- POST /admin/webhooks/regenerate_token — nytt token
- POST /admin/webhooks/delete — slett webhook

Frontend:
- /admin/webhooks side med full CRUD
- Vis token, mål-samling, hendelsesteller, siste aktivitet
- Kopier token/URL til utklippstavle
- Utfellbar hendelseslogg per webhook med payload-visning
- Regenerer token med bekreftelse
- Slett med bekreftelse
- Nav-lenke fra admin-hub
2026-03-18 21:55:24 +00:00
8c77b60561 Webhook-endepunkt: POST /api/webhook/<token> → content-node (oppgave 29.4)
Nytt offentlig endepunkt som mottar vilkårlig JSON og oppretter en
content-node i målsamlingen. Webhook-noder har et unikt token i
metadata som brukes til autentisering i stedet for JWT.

Flyten: token-oppslag → finn belongs_to-edge til samling →
opprett content-node med payload i metadata → belongs_to-edge →
tilgangspropagering fra samling.

Trekker ut title/content fra payload automatisk når feltene finnes.
2026-03-18 21:41:22 +00:00
7fbdc3f5dc Feed-orkestrering: periodisk RSS/Atom-polling per samling (oppgave 29.3)
Ny standard-orkestrering "Overvåk RSS-feed" som bruker synops-feed CLI.
Samlinger konfigurerer feed-abonnementer via metadata.feed_subscriptions[],
med konfigurerbar URL, intervall og mål (inbox/channel).

Komponenter:
- Migration 030: synops-feed cli_tool-seed, orchestration-seed, indeks, prioritetsregel
- feed_poller.rs: Bakgrunnstask som hvert 60s finner forfalne abonnementer
  og enqueuer feed_poll-jobber. Dedupliserer mot kjørende jobber.
- feed_poll job handler: Spawner synops-feed CLI, oppdaterer last_polled_at
- API: configure_feed_subscription + remove_feed_subscription endepunkter

Verifisert: NRK toppsaker.rss → 100 noder opprettet, last_polled_at oppdatert.
2026-03-18 21:32:00 +00:00
1d8ebf259b Skjermklipp-input: paste screenshot i chat → CAS → media-node (oppgave 29.1)
Frontend:
- ChatInput: paste-handler detekterer bilder fra clipboard (ClipboardEvent),
  laster opp til CAS via uploadMedia med metadata_extra { source: "screenshot" }
- Chat-side: viser bildenoder inline med AI-beskrivelse når tilgjengelig
- api.ts: uploadMedia støtter nå metadata_extra for ekstra node-metadata

Backend (maskinrommet):
- upload_media: nytt metadata_extra multipart-felt som merges inn i
  media-nodens metadata (f.eks. source, description)
- describe_image: ny jobbtype — enqueuues automatisk for screenshot-uploads,
  kaller synops-ai med --image for AI-beskrivelse av bildet
- Beskrivelsen lagres tilbake i media-nodens metadata.description

synops-ai:
- Nytt --image flag for multimodal LLM-kall (vision) via LiteLLM
- Sender bilde som base64 data-URL i OpenAI-kompatibelt format
- Brukes av describe_image-jobben for bildbeskrivelse
2026-03-18 21:07:00 +00:00
2fa5d7ef2f AI-kostnadstak per bruker/samling: budsjettsjekk før AI-kall (oppgave 28.3)
Samlings- og brukernoder kan nå ha ai_budget i metadata:
  { "ai_budget": { "monthly_limit_usd": 50.0 } }

Før hvert AI-kall aggregeres inneværende måneds forbruk fra
ai_usage_log og sammenlignes med grensen. Ved overskridelse:
- AI-kallet blokkeres med feilmelding
- En work_item-node opprettes med tag "budget_exceeded"
- Work_item knyttes til samlingen via belongs_to-edge

Endringer:
- migrations/029: requested_by-kolonne i ai_usage_log + indekser
- synops-ai: --collection-id/--user-id flagg, budsjettsjekk i prompt
- maskinrommet/ai_budget.rs: delt budsjettsjekk-modul
- maskinrommet/ai_process.rs: budsjettsjekk før AI gateway-kall
- docs/infra/ai_gateway.md: oppdatert § 6.3 fra "fase 2" til implementert
2026-03-18 20:19:52 +00:00
25fc1a1b59 Username i auth_identities: Authentik-synk ved login (oppgave 26.1)
Legger til username-kolonne i auth_identities med UNIQUE constraint.
Ved innlogging sender SvelteKit preferred_username fra Authentik til
maskinrommet POST /auth/sync, som oppdaterer kolonnen. Grunnlaget
for epost-ruting i fase 26: vegard@synops.no → username-oppslag.
2026-03-18 19:04:57 +00:00
8af4265b6e synops-clip orkestrering-støtte: cli_tool-registrering + clip_url jobb/API (oppgave 25.4)
Gjør synops-clip tilgjengelig i orkestreringer ved å:

1. Registrere synops-clip som cli_tool-node (migration 026) med norske
   aliases (clip, klipp, hent artikkel) og args_hints for script-kompilatoren.
   Orkestreringer kan nå skrive "1. clip fra event (lagre node, bruker)"
   som kompileres til "synops-clip --url {event.url} --write --created-by ...".

2. Legge til clip_url som jobbtype i jobbkøen (clip.rs) — spawner
   synops-clip med riktige env-variabler (DATABASE_URL, AI_GATEWAY_URL, etc).

3. Legge til POST /intentions/clip_url API-endepunkt slik at frontend
   og andre klienter kan trigge URL-klipping direkte.

4. Utvide trigger-konteksten med event.url og event.created_by slik at
   orkestreringer som reagerer på URL-deling kan videresende URL til
   synops-clip via variabel-substitusjon.
2026-03-18 18:55:11 +00:00
d18dfc260f AI-assistert oppretting: synops-ai genererer orkestreringsscript fra fritekst (oppgave 24.7)
Nytt CLI-verktøy `synops-ai` som leser cli_tool-noder fra PG, bygger
en systemprompt med tilgjengelige verktøy og script-grammatikk, og
bruker LLM til å foreslå orkestreringsscript fra fritekst-beskrivelse.

Tre moduser:
- Synkron: --description "..." → LLM genererer script → JSON output
- System prompt: --generate-system-prompt → skriver auto-generert prompt
- Eventually: --eventually → lagrer som work_item for Claude Code

Maskinrommet: nytt endepunkt POST /intentions/ai_suggest_script som
kaller synops-ai, validerer resultatet med script_compiler, og returnerer
script + kompileringsresultat til frontend.

Frontend: AI-assistent-knapp i OrchestrationTrait med fritekst-input,
generer-knapp, og feilvisning. Generert script settes direkte i editoren.

Migration: synops-ai seeded som cli_tool-node med norske verb-alias.
2026-03-18 17:47:32 +00:00
376bf7ee62 Orchestration UI: editor med tre visninger, sanntids kompilering, testkjøring (oppgave 24.6)
Backend:
- POST /intentions/compile_script — kompilerer script, returnerer diagnostikk + kompilert resultat
- POST /intentions/test_orchestration — trigger manuell testkjøring via jobbkø
- GET /query/orchestration_log — henter kjørehistorikk for en orkestrering
- "orchestration" lagt til som gyldig trait for samlingsnoder

Frontend:
- OrchestrationTrait.svelte — BlockShell-panel med:
  - Tre tabber: Enkel (editor), Teknisk (kompilert CLI), Kompilert (JSON)
  - Sanntids kompilering med 500ms debounce og diagnostikk-visning
  - Trigger-velger (6 event-typer) og executor-velger (script/bot/dream)
  - "Test kjøring"-knapp (lagrer + oppretter testjobb i køen)
  - Kjørehistorikk-panel med steg-status, varighet, feilmeldinger
  - Responsiv: container queries + mobile viewport fallback
- Registrert i collection-page (mobil + desktop), workspace/types.ts
- API-funksjoner: compileScript, testOrchestration, fetchOrchestrationLog
2026-03-18 17:30:52 +00:00
059c776bf4 Script-executor: vaktmesteren eksekverer kompilerte pipelines (oppgave 24.5)
Ny modul script_executor.rs som tar en kompilert pipeline fra
script_compiler og kjører stegene sekvensielt:

- Substituerer {event.*}-variabler fra trigger-kontekst
- Spawner hvert CLI-verktøy som subprosess via generisk dispatch
- VED_FEIL-håndtering: steg-fallback → global fallback → stopp
- Spesialhåndtering av work_item (oppretter oppgave-node i grafen)
- Logger hvert steg i ny orchestration_log-tabell

handle_orchestrate i jobs.rs utvides: kompilerer + utfører i
samme jobb (var tidligere kun kompilering).

Migration 023: orchestration_log-tabell med indekser for
effektiv spørring per orkestrering og per jobb.
2026-03-18 17:18:10 +00:00
cc23e26802 Script-kompilator for orkestreringer (oppgave 24.3)
Parser menneskelig scriptspråk og kompilerer til tekniske CLI-kall.
"transkriber lydfilen (stor modell)" → "synops-transcribe --cas-hash {event.cas_hash} --model large"

Kompilatoren:
- Parser nummererte steg, ved_feil-fallbacks, og globale feilhåndterere
- Matcher verb mot cli_tool-noders aliases (case-insensitive)
- Mapper argumenter i parentes via args_hints
- Validerer variabelreferanser ({event.*}) mot kjent liste
- Fuzzy-matching med Levenshtein-avstand for forslag ved feil
- Rust-stil kompileringsrapport med ✓/✗ per linje

Integrert i jobbkøen: orchestrate-jobb kompilerer scriptet og
lagrer pipeline i metadata. Utførelse kommer i oppgave 24.5.

12 unit-tester dekker parser, kompilator, feilhåndtering og fuzzy-matching.
2026-03-18 17:03:47 +00:00
26f03ef21d Trigger-evaluering i portvokteren (oppgave 24.2)
Ved node/edge-events fra PG LISTEN/NOTIFY evaluerer portvokteren nå
om noen orchestration-noder matcher triggeren. Implementert som non-blocking
async task som ikke blokkerer WebSocket-flyten.

Ny modul orchestration_trigger.rs:
- Mapper NOTIFY-events til trigger-typer (node.created, edge.created)
- Effektiv lookup via funksjonell B-tree-indeks på metadata->trigger->event
- Evaluerer observes-edges (eksplisitt) vs conditions (implisitt)
- Betingelser: node_kind, edge_type, has_trait, has_tag (AND-logikk)
- Legger matchende orkestreringer i jobbkøen som "orchestrate"-jobb

Ny migration 021: indeks for trigger-event lookup på orchestration-noder.
Jobbkø-dispatcher håndterer "orchestrate" med placeholder (24.3 implementerer utførelse).

Verifisert: content-node trigrer matching orchestration, communication-node hoppes over.
2026-03-18 16:53:59 +00:00
b5aa5bb243 Fjern SpacetimeDB komplett (oppgave 22.4)
SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av
PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet).

Kode fjernet:
- spacetimedb/ Rust-modul og spacetime.json
- maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers)
- frontend module_bindings/ (23 auto-genererte filer)
- spacetimedb npm-avhengighet fra package.json
- scripts/test-sanntid.sh (testet STDB-flyt)

Infrastruktur:
- Docker-container stoppet og fjernet fra docker-compose.yml
- Caddy: fjernet /spacetime/* reverse proxy
- maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler
- .env.example: fjernet SpacetimeDB-seksjoner

Dokumentasjon oppdatert:
- CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell
- docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg
- docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet"
- 37 andre docs oppdatert (features, concepts, infra, ops, retninger)
- Alle kode-kommentarer med STDB-referanser oppdatert

Verifisert: maskinrommet bygger og starter OK, frontend bygger OK,
helsesjekk returnerer 200. Caddy reloadet.
2026-03-18 13:39:09 +00:00
aee6adc425 Fjern STDB-skrivestien: all skriving går kun til PG (oppgave 22.3)
SpacetimeDB var brukt som «instant feedback»-lag mellom portvokteren
og frontend. Nå som PG NOTIFY-triggere og WebSocket er på plass
(oppgave 22.1–22.2), er STDB-skrivestien overflødig.

Endringer:
- intentions.rs: Alle CRUD-operasjoner (create/update/delete node/edge)
  skriver nå synkront til PG i stedet for STDB-først + async PG-jobbkø.
  PG NOTIFY-triggere gir umiddelbar sanntidsoppdatering til klienter.
  Tilgangsgivende edges (owner/admin/member_of/reader) bruker transaksjon
  med recompute_access direkte i handleren.
- maintenance.rs: Fjernet StdbClient fra alle funksjoner. Varsler
  opprettes/oppdateres/slettes direkte i PG.
- agent.rs, audio.rs, tts.rs, ai_process.rs: Fjernet STDB-synk etter
  CLI-verktøy-kjøring. PG NOTIFY dekker sanntidsvisning.
- pg_writes.rs: Fjernet sync_node_access_to_stdb. access_changed
  NOTIFY-trigger håndterer dette.
- workspace.rs: Synkrone PG-skrivinger med recompute_access.
- summarize.rs, ai_edges.rs: Fjernet StdbClient fra signaturer.
- jobs.rs: Fjernet StdbClient fra dispatch og start_worker.
- main.rs: Fjernet STDB-initialisering, warmup, stdb_monitor.
  StdbClient fjernet fra AppState. stdb.rs beholdt som død kode
  (fjernes i oppgave 22.4).
- health.rs: Fjernet STDB-helsesjekk fra dashboard.
- Slettet warmup.rs og stdb_monitor.rs (PG→STDB-synk ikke lenger
  relevant).
- docs/retninger/datalaget.md: Markert fase M3 som fullført.
2026-03-18 13:11:33 +00:00
fcc9e671a5 Backend for SpacetimeDB-migrering: berikede WS-events + mixer-API
Fase M2 (oppgave 22.2): Portvokteren sender nå full raddata i
WebSocket-events (ikke bare ID), slik at frontend kan oppdatere
stores direkte uten ekstra API-kall.

Endringer:
- ws.rs: Berik node/edge/access-events med full PG-data etter NOTIFY
- ws.rs: Ny mixer_channel_changed event-type + initial_sync inkluderer mixer
- ws.rs: Resync ved lag (broadcast overflow)
- mixer.rs: Nye HTTP-endepunkter som erstatter STDB-reducers
  (create_mixer_channel, set_gain, set_mute, toggle_effect, set_mixer_role)
- 019_mixer_channels.sql: PG-tabell + NOTIFY-trigger for mixer-tilstand
2026-03-18 12:16:36 +00:00
0ecb7104c0 WebSocket-lag i portvokteren: PG LISTEN/NOTIFY + WS-endepunkt (oppgave 22.1)
Implementerer Fase M1 av SpacetimeDB-migrasjonen:

- SQL-migrasjon 018: Triggers for notify_node_change, notify_edge_change
  og notify_access_change på nodes, edges og node_access-tabellene
- Ny ws.rs-modul i maskinrommet med:
  - PG LISTEN bakgrunnsoppgave som lytter på tre kanaler
  - Broadcast-kanal for å videresende events til alle WS-klienter
  - WebSocket-endepunkt (/ws) med JWT-autentisering
  - Initiell snapshot (initial_sync) ved tilkobling
  - Tilgangskontrollfiltrering per klient via node_access-matrisen
- Oppdatert AppState med WsBroadcast og /ws-rute

Frontend dual-tilkobling (STDB + nytt WS) kommer i neste commit.
2026-03-18 11:54:34 +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
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
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
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
4eef7d79bb Implementer personlig arbeidsflate (oppgave 19.6)
Brukerens standard arbeidsflate med node_kind='workspace'.
Vises på /workspace når ikke koblet til en samling.

Backend:
- GET /my/workspace: finn eller opprett brukerens workspace-node
- Automatisk provisjonering ved første besøk (STDB + async PG)
- Owner-edge fra bruker til workspace

Frontend:
- /workspace rute med Canvas + BlockShell (gjenbruker spatial canvas)
- Fritt valg av verktøy-paneler fra verktøymeny
- Layout persisteres i workspace-nodens metadata via updateNode
- Tom-tilstand med verktøy-velger for nye brukere
- Responsivt: stacked tabs på mobil, spatial canvas på desktop
- Kontekst-velger i header for navigering til samlinger

Navigasjon:
- "Min flate"-knapp på mottak-siden
- "Min arbeidsflate" i ContextHeader dropdown for samlingssider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:52:52 +00:00
8b5425fb59 Fullfører oppgave 18.6: Egendefinerte AI-presets
Brukere kan nå opprette egne AI-preset-noder med custom prompt,
dele dem med samlinger/team via shared_with-edges, og redigere/slette
egne presets. Modellprofil (flash/standard) er låst — kun admin
kan oppgradere fra flash til standard.

Backend:
- POST /intentions/create_ai_preset: Oppretter custom preset med
  tvunget category=custom og model_profile=flash. Valgfri deling
  til samling i samme kall.
- update_node: Beskytter model_profile mot endring av ikke-admin.
  Forhindrer kategori-endring fra custom til standard.

Frontend:
- AiToolPanel: "+ Nytt preset"-knapp, opprett/rediger-skjema med
  tittel, prompt, retning, ikon og farge. Rediger/slett/del-knapper
  for egne custom presets. Egendefinerte presets markert med "egn."
- createAiPreset() i api.ts.

Dokumentasjon:
- ai_verktoy.md: Oppdatert fasestatus, ny § 10 om egendefinerte
  presets med API-eksempler og modellprofil-beskyttelse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:11:34 +00:00
bca0ff1deb Fullfører oppgave 18.2: AI-prosessering endepunkt
POST /intentions/ai_process med source_node_id, ai_preset_id og
direction (node_to_tool / tool_to_node).

Endepunktet validerer input, sjekker at kilde-node og AI-preset
finnes, verifiserer skrivetilgang for tool_to_node-retning, og
legger en ai_process-jobb i køen.

Jobb-handleren (ai_process.rs) henter kilde-content og preset-prompt,
mapper modellprofil → LiteLLM-alias (flash → sidelinja/rutine,
standard → sidelinja/resonering), kaller AI Gateway, og logger
forbruk i både ai_usage_log og resource_usage_log.

Direction-logikk (opprett ny node vs. oppdater eksisterende)
implementeres i oppgave 18.3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:24:31 +00:00
a3cdfa9dc2 Fullfører oppgave 17.7: FFmpeg feilmeldinger til bruker
Tre endringer som sammen gir brukeren innsyn i FFmpeg-feil:

1. Backend: Nytt GET /query/job_status-endepunkt i queries.rs.
   Frontenden pollet allerede denne URLen, men endepunktet manglet.
   Returnerer status, result og error_msg fra job_queue.

2. RenderDialog: Ny error-tilstand med formatFfmpegError() som
   trekker ut lesbar feilmelding fra FFmpeg stderr-dump. Viser
   kort oppsummering + ekspanderbar full feilmelding via <details>.

3. Studio-side: Sender renderError til RenderDialog som errorMessage.
   Toast-varselet vises kun når dialogen er lukket (unngår duplisering).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:03:59 +00:00
ce56e31de2 Fullfører oppgave 17.6: Periodisk CAS tmp-opprydding
Legger til cleanup_tmp() i CasStore som sletter orphaned .tmp-filer
eldre enn 1 time. Disse oppstår når en skriveprosess krasjer midt i
en atomisk CAS-skriveoperasjon (skriv til tmp, rename til endelig path).

Ny bakgrunnsloop start_tmp_cleanup_loop() kjører hver time og fjerner
foreldede temp-filer. Følger samme mønster som pruning- og
disk-monitor-loopene.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 05:57:10 +00:00
5771d1eed6 Fullfører oppgave 15.9: Brukersynlig forbruk
Legger til to nye API-endepunkter for brukersynlig ressursforbruk:
- GET /my/usage — brukerens eget forbruk (filtrert på triggered_by)
- GET /query/node_usage — forbruk for én node (kun eier/admin)

Frontend:
- /profile — profilside med grafstatistikk og forbruksoversikt
- NodeUsage-komponent integrert i samlings-detaljsiden
- Brukernavn i header lenker nå til profilsiden

Tilgangssjekk: nodeforbruk krever created_by eller owner/admin-edge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:42:47 +00:00
d7dffa06e6 Fullfører oppgave 15.8: Forbruksoversikt i admin
Aggregert ressursforbruk-dashboard som spør mot resource_usage_log
(oppgave 15.7). Tre visninger: totaler per ressurstype, per samling,
og daglig tidsserie. AI drill-down viser forbruk per jobbtype og
modellnivå (fast/smart/deep).

Backend: GET /admin/usage med days- og collection_id-filtre.
Frontend: /admin/usage med filterbare tabeller og fargekodede kort.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:34:08 +00:00
eb81055ef4 Fullfører oppgave 15.7: Ressursforbruk-logging
Sentralisert logging av alle ressurskrevende operasjoner til
resource_usage_log-tabellen (opprettet i migrasjon 009).

Ny kode:
- resource_usage.rs: hjelpemodul med log() og find_collection_for_node()
- bandwidth.rs: Caddy JSON-logg-parser med nattlig batch-jobb (kl 03:00)

Logging lagt til i handlere:
- AI: summarize, ai_edges (token-telling via LiteLLM usage-felt),
  agent (placeholder — claude CLI gir ikke token-info)
- Whisper: duration_seconds, model, language, mode
- TTS: refaktorert til sentralisert modul, lagt til collection_id
- CAS: logger nye filer ved upload (ikke dedup)
- LiveKit: logger join-hendelser (faktisk deltaker-minutter
  krever webhook-integrasjon i fremtiden)

Caddy-config: JSON access logging aktivert for sidelinja.org og
synops.no i /srv/synops/config/caddy/Caddyfile (utenfor repo).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:24:54 +00:00
56b7df8bf8 Fullfører oppgave 15.6: Serverhelse-dashboard
Nytt admin-dashboard for sanntids serverhelse med fire hoveddeler:

1. Tjeneste-status: Parallelle helsesjekker for alle 7 tjenester
   (PG, STDB, Caddy, Authentik, LiteLLM, Whisper, LiveKit) med
   latens-måling og statusrapportering (up/down/degraded).

2. System-metrikker: CPU-load via /proc/loadavg, minne via
   /proc/meminfo, disk via statvfs, oppetid via /proc/uptime.
   Vises med progress-bars og fargekodede terskler.

3. PG-statistikk: Aktive tilkoblinger, maks-tilkoblinger,
   databasestørrelse og aktive spørringer.

4. Logg-tilgang: Filtrerbar visning av logger fra alle tjenester.
   Bruker journalctl for systemd-tjenester og docker logs for
   containere. Konfigurerbart antall linjer per tjeneste.

Backend: health.rs med tokio::join! for parallelle sjekker.
Frontend: /admin/health med auto-polling hvert 10. sekund.
Backup-sjekk rapporterer ok/stale/missing (ingen backup satt opp ennå).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:12:54 +00:00
b64f217637 Fullfører oppgave 15.5: Ressursstyring for jobbkø
Implementerer prioritetsregler, ressursgrenser og LiveKit-bevisst
resource governor for jobbkø-workeren, pluss disk-overvåking med varsling.

Hovedkomponenter:

1. Prioritetsregler (job_priority_rules-tabell):
   - Konfigurerbar base_priority, cpu_weight, max_concurrent per jobbtype
   - LiveKit-justering: livekit_priority_adj og block_during_livekit
   - Timeout per jobbtype
   - Admin-API for å endre regler uten restart

2. Ressurs-governor i worker-loopen:
   - Semaphore: maks 3 samtidige jobber
   - CPU-vektgrense: total vekt maks 8 (Whisper=5, render=1, etc.)
   - Per-type concurrency-grense
   - LiveKit-status sjekkes med 10s cache-TTL
   - Jobber utsettes/nedprioriteres ved aktive LiveKit-rom
   - Individuell timeout per jobb (default 600s)
   - Jobber kjøres i egne tokio-tasks (parallell dispatch)

3. Disk-overvåking:
   - Sjekker diskbruk hvert 60. sekund via statvfs
   - Terskler: 85% warning, 90% critical, 95% emergency
   - Logger til disk_status_log (siste 1000 målinger beholdes)
   - Admin-API: GET /admin/resources/disk med historikk

4. Admin-API:
   - GET /admin/resources — samlet ressursstatus
   - GET /admin/resources/disk — diskstatus med historikk
   - POST /admin/resources/update_rule — oppdater prioritetsregel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:02:11 +00:00
3fbe42f207 Fullfører oppgave 15.4: AI Gateway admin-UI
Implementerer /admin/ai med fire faner:
- Modeller & fallback: oversikt over aliaser med fallback-kjeder,
  toggle aktiv/inaktiv, legg til/fjern modeller, endre prioritet
- Ruting: jobbtype → modellalias mapping med dropdown-endring
- Forbruk: aggregert tokenforbruk per samling/alias/jobbtype
- API-nøkler: viser hvilke env-variabler som er satt (aldri verdier)

Backend (maskinrommet/src/ai_admin.rs):
- GET /admin/ai — full oversikt med aliaser, providers, ruting, forbruk
- GET /admin/ai/usage — forbruk med filtre (dager, samling)
- POST-endepunkter for CRUD på aliaser, providers og ruting-regler

PG er single source of truth. API-nøkler lagres kun som env-variabler,
admin-panelet viser bare om de er satt eller mangler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:51:45 +00:00
07fa10620b Fullfører oppgave 15.3: Jobbkø-oversikt med admin-UI
Admin kan nå se, filtrere, retrye og avbryte jobber via /admin/jobs.

Backend:
- jobs.rs: list_jobs(), count_by_status(), distinct_job_types(),
  retry_job(), cancel_job() for admin-spørringer
- intentions.rs: GET /admin/jobs, POST retry_job/cancel_job handlers
- main.rs: tre nye ruter

Frontend:
- /admin/jobs: statusoppsummering med antall per status, filter på
  type/status, paginert tabell med retry/avbryt-knapper, 5s polling
- /admin: navigasjonslenke til jobbkø

Migrasjon:
- 013_job_queue.sql: formaliserer job_queue-tabellen med admin-indekser

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:40:56 +00:00
d8e44fe57e Fullfører oppgave 15.2: Graceful shutdown med vedlikeholdsmodus
Implementerer koordinert nedstenging der admin setter et
vedlikeholdstidspunkt, brukere ser nedtelling, og systemet
stenger ned trygt etter at aktive jobber er ferdige.

Nye filer:
- maskinrommet/src/maintenance.rs — MaintenanceState med atomiske
  flagg, shutdown-koordinator (vent på scheduled_at → blokker
  nye jobber/LiveKit → vent på kjørende jobber → exit)
- frontend/src/routes/admin/+page.svelte — admin-panel for
  vedlikehold med statusvisning og aktive sesjoner

Endringer:
- jobs.rs: sjekker maintenance.is_active() før dequeue
- intentions.rs: nye endepunkter (initiate/cancel/status), blokkerer
  join_communication under vedlikehold
- main.rs: MaintenanceState i AppState, nye ruter
- api.ts: klientfunksjoner for maintenance-API
- adminpanelet.md: dokumenterer implementerte endepunkter

Flyt: admin → GET /admin/maintenance_status (se aktive sesjoner)
→ POST /intentions/initiate_maintenance → varsel broadcast via STDB
→ frontend nedtelling → scheduled_at nådd → active=true → jobbkø
pauset + LiveKit blokkert → vent maks 5 min → process::exit(0)
→ systemd restarter maskinrommet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:31:32 +00:00
831666012a Fullfører oppgave 15.1: Systemvarsler via STDB
Implementerer systemvarsler (system_announcement-noder) som vises som
banner/toast for alle aktive klienter i sanntid via SpacetimeDB.

Backend (maskinrommet):
- POST /intentions/create_announcement: oppretter varslingsnode med
  validering av announcement_type (info/warning/critical), datoer
  (scheduled_at, expires_at) og blocks_new_sessions. Visibility
  settes automatisk til 'open' for broadcast til alle klienter.
- POST /intentions/expire_announcement: fjerner varslingsnode med
  tilgangskontroll (kun eier kan slette).

Frontend:
- SystemAnnouncements.svelte: filtrerer nodeStore for aktive
  system_announcement-noder, skjuler utløpte (expires_at), viser
  nedtelling for planlagte hendelser (scheduled_at). Fargekoding:
  rød (critical), gul (warning), blå (info). Critical kan ikke
  dismisses. Oppdaterer nedtelling hvert sekund.
- Komponent lagt til i +layout.svelte for global synlighet.
- API-funksjoner (createAnnouncement, expireAnnouncement) i api.ts.

Ref: docs/concepts/adminpanelet.md § "Systemvarsler og vedlikeholdsmodus"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:21:18 +00:00
1425a82cdd Fullfører oppgave 14.17: A/B-testing for presentasjonselementer
Implementerer automatisk A/B-testing for forside-varianter:

- PG-migrasjon 012: ab_events-tabell for impression/klikk-logging
  med hour_of_week (0-167) for tidspunkt-normalisering
- Variant-rotasjon: ab_select() velger tilfeldig blant testing-varianter
  ved forside-rendering, winner prioriteres, retired filtreres bort
- Impression-logging: asynkron fire-and-forget ved forside-serve
  (både cache-hit og -miss), lagres i ab_events
- Klikk-attribusjon: artikkelbesøk sjekker forside-cache for aktive
  AB-varianter og logger klikk. Eksplisitt tracking via
  GET /pub/{slug}/t/{article_id}?v={edge_id}
- Periodisk evaluator (300s intervall): z-test for proporsjoner
  (p < 0.05), minimum 100 impressions per variant, oppdaterer
  edge-metadata (ab_status, impressions, clicks, ctr)
- Redaktør-overstyring: POST /intentions/ab_override markerer
  valgt variant som winner, andre som retired (krever owner/admin)
- Auto-initialisering: maybe_start_ab_test() setter ab_status=testing
  automatisk når >1 variant av samme type opprettes

Alle 42 tester passerer inkludert 3 nye z-test-tester.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:13:39 +00:00
63630eb55a Fullfører oppgave 14.16: Presentasjonselementer som noder
Publisert tittel, ingress, OG-bilde og undertittel er nå egne noder
koblet til artikler via title/subtitle/summary/og_image-edges.
Rendering bruker presentasjonselementer med fallback til artikkelfelt.

Backend:
- Ny query: GET /query/presentation_elements?article_id=...
- render_article_to_cas henter presentasjonselementer via edges
- fetch_article + fetch_index_articles bruker pres.elementer
- Batch-henting for forsideartikler (én SQL-spørring)
- ArticleData utvides med subtitle + og_image
- Alle fire temaer viser subtitle og OG-bilde
- SEO og_image-tag fylles fra presentasjonselement

Frontend:
- PresentationEditor.svelte: opprett/rediger tittel, undertittel,
  ingress, OG-bilde med variantvelger (editorial/ai/social/rss)
- Integrert i PublishDialog via <details>-seksjon
- API-klient: fetchPresentationElements(), deleteNode()

Grunnlag for A/B-testing (oppgave 14.17): edge-metadata støtter
ab_status/impressions/clicks/ctr, best_of() prioriterer winner > testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 02:55:23 +00:00
26c6a3b8d9 Dynamiske sider (oppgave 14.15): kategori, arkiv, søk, om-side
Implementerer fire nye dynamiske sidetyper for publiseringssamlinger:

- Kategori-sider: filtrert på tag-edges, paginert med cache
- Arkiv: kronologisk med månedsgruppering, paginert med cache
- Søk: PG fulltekst med tsvector/ts_rank, paginert med cache
- Om-side: statisk CAS-node (page_role: "about"), immutable cache

Teknisk:
- Ny migrasjon (011): tsvector-kolonne + GIN-indeks + trigger for søk
- Nye Tera-templates: category.html, archive.html, search.html, about.html
- DynamicPageCache for in-memory caching av dynamiske sider
- Ruter for /pub/{slug}/kategori/{tag}, arkiv, sok, om
- Custom domain-varianter for alle nye sidetyper

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 02:39:06 +00:00
eea66744d8 Planlagt publisering (oppgave 14.12): periodisk scheduler i maskinrommet
Legger til en bakgrunns-scheduler som hvert 60. sekund sjekker for
belongs_to-edges med publish_at i fortiden der artikkelen ikke er rendret.
Ved treff: oppretter render_article-jobb (og render_index for berørte
samlinger) i jobbkøen. RSS-feeden oppdateres automatisk siden den
genereres dynamisk fra DB.

Detaljer:
- find_due_articles(): SQL-spørring med deduplisering mot eksisterende jobber
- run_publish_scheduler(): orkestrerer én runde med publisering
- start_publish_scheduler(): tokio::spawn med 60s poll-intervall
- Hooks inn i main.rs ved oppstart, parallelt med jobbkø og pruning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 02:14:04 +00:00
f1752e73f5 Redaktørens arbeidsflate (oppgave 14.11): Kanban-brett for innsendinger
Redaktøren ser alle artikler med submitted_to-edge til samlingen,
gruppert i fire kolonner etter status. Drag-and-drop mellom kolonner
endrer status via update_edge. Siste kolonne ("Planlagt") åpner en
dialog for å sette publish_at i edge-metadata — klar for oppgave 14.12
(planlagt publisering).

Backend: GET /query/editorial_board med forfatterinfo og edge-metadata.
Frontend: /editorial/[id] med sanntidsoppdateringer via SpacetimeDB.
Lenke fra PublishingTrait når require_approval er aktivt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 02:09:03 +00:00