Commit graph

116 commits

Author SHA1 Message Date
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
6aeb8aa783 Podcast-trait admin-UI og utvidet RSS-metadata (oppgave 30.2)
Dedikert admin-UI for podcast-trait med riktige skjemafelt:
- iTunes Author, Category (med underkategori-dropdown), Language
- Explicit-avkrysning, Redirect Feed URL
- Erstatter generisk nøkkel/verdi-editor for podcast-traitet

RSS-utvidelser:
- itunes:category støtter nå nested subcategory-element
- itunes:new-feed-url for feed-migrasjon via redirect_feed
- Oppdatert både maskinrommet og synops-rss CLI-verktøy
2026-03-18 23:21:15 +00:00
d7f08d439d iTunes/Podcasting 2.0 RSS-tags: komplett implementering (oppgave 30.1)
Utvider synops-rss og maskinrommet/src/rss.rs med iTunes og Podcasting 2.0
namespace for podcast-samlinger.

Channel-level tags:
- itunes:author, itunes:category, itunes:explicit fra podcast-trait metadata
- itunes:image fra samlingens og_image-edge (CAS-hash)
- itunes:type (episodic)
- podcast:locked

Item-level tags:
- itunes:title, itunes:duration (fra media-metadata duration_secs)
- itunes:explicit (arver fra kanal), itunes:image (episode og_image)
- podcast:transcript (SRT-URL hvis transcription_segments finnes)
- podcast:chapters (JSON-URL hvis chapter-edges finnes)

DB-spørringene er utvidet til å hente transkripsjons-eksistens,
varighet, episode-bilde og kapitler i effektive batch-spørringer.

Merk: Transcript/chapters-URL-ene genereres i feeden men krever
offentlige endepunkt for å serveres (fremtidig oppgave).
2026-03-18 23:12:34 +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
a06b79478a AI-rutingskontroll i admin: 13 kontekster konfigurerbare uten redeploy (oppgave 28.2)
Utvider /admin/ai med full kontroll over hvilken modellalias som brukes
per AI-kontekst. Admin kan bytte modell for orkestrering, bot-chat,
oppsummering, edge-forslag, klassifisering osv. uten å restarte
maskinrommet.

Endringer:
- Migration 028: seeder 7 nye kontekster i ai_job_routing
  (orchestration_script/dream, bot_chat/triage, summarize, suggest_edges, classify)
- Backend: resolve_routing_or_default() i ai_admin.rs — felles oppslag
  mot ai_job_routing med fallback til sidelinja/rutine
- Dispatchers (ai_edges, summarize) bruker nå routing-tabellen i stedet
  for hardkodede env-variabler — endringer trer i kraft umiddelbart
- Frontend: Ruting-tab omskrevet med kategoriserte kontekster
  (Orkestrering, Bot & chat, Analyse, Prosessering), beskrivelser per
  kontekst, og støtte for egendefinerte regler
- Docs: ai_gateway.md §3.4 oppdatert med alle 13 kontekster
2026-03-18 20:06:50 +00:00
8f7d2d7fe8 MindMap-trait for samlingsnoder (oppgave 27.3)
Gjør mindmap til et offisielt trait som kan velges ved opprettelse
av samlingsnoder. Frontend-komponenten (27.1) og BlockShell-panelet
(27.2) var allerede på plass — dette kobler traiten inn i backend-
validering og pakke-definisjoner.

Endringer:
- Lagt til "mindmap" i VALID_TRAITS (intentions.rs)
- Validering av konfig: default_depth 1-3, layout radial/tree
- Inkludert mindmap i Podcaststudio- og Wiki-pakker (traits.ts)
- Oppdatert trait-katalog i docs/primitiver/traits.md
2026-03-18 19:44:30 +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
f22465d72b @bot URL-klipping i chat: synops-clip-integrasjon (oppgave 25.3)
Når en bruker limer inn en URL i chatten, gjenkjenner synops-respond
URL-en automatisk, kaller synops-clip --write for å hente, parse og
oppsummere artikkelen, og inkluderer resultatet i prompten slik at
Claude kan presentere oppsummeringen naturlig.

Ved betalingsmur: Claude informerer brukeren og ber om innlimt innhold.
Maks 3 URL-er per melding, 60s timeout per klipp.

Endringer:
- synops-respond: URL-deteksjon (regex), synops-clip-kall, prompt-kontekst
- maskinrommet/agent.rs: videresend env-variabler for synops-clip
- maskinrommet-env.sh: SYNOPS_CLIP_SCRIPTS env-variabel
- docs/infra/claude_agent.md: dokumentert URL-klipping-flyten
2026-03-18 18:41:33 +00:00
1f21f90f76 Seed-orkestreringer og flerords-verbstøtte (oppgave 24.9)
Fem standard-orkestreringer opprettet som seed-data:
- Podcast-pipeline (transkriber → oppsummer → RSS)
- Publiseringsflyt (render → indeks → RSS)
- AI-beriking (foreslå koblinger ved nytt innhold)
- Planlagt publisering (render ved tidspunkt)
- Podcast TTS (kaskade fra pipeline → les opp oppsummering)

Podcast-pipeline → TTS demonstrerer kaskade via triggers-edge.

Script-kompilatoren utvidet med flerords-verbstøtte: aliaser som
"generer feed", "les opp", "foreslå koblinger" matcher nå korrekt
selv om parseren splitter ved første mellomrom. Prøver verb + N
første ord av objekt opptil 3 ord.
2026-03-18 18:11:02 +00:00
44327df370 Kaskade: triggers-edge mellom orkestreringer (oppgave 24.8)
Etter vellykket pipeline-utførelse sjekker handle_orchestrate() om
orkestreringen har utgående triggers-edges til andre orchestration-noder.
Hvert gyldig mål enqueues som ny orchestrate-jobb med kaskade-kontekst.

Syklusdeteksjon via cascade_chain i jobb-payload:
- Sporer alle orchestration-IDer allerede utført i kjeden
- Blokkerer target som allerede finnes i kjeden (direkte + indirekte syklus)
- Dybdegrense på 10 ledd (MAX_CASCADE_DEPTH)
- Blokkerte kaskader logges i orchestration_log med status=skipped

Nedstrøms orkestreringer mottar:
- trigger_event: "cascade"
- upstream_orchestration_id i trigger_context
- {event.upstream_orchestration_id} tilgjengelig i script

Kaskade-feil er ikke-fatale — selve orkestreringen rapporteres som suksess.

8 nye enhetstester for syklusdeteksjon og dybdegrense.
2026-03-18 17:57:26 +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
6dabdcf8ec Legg til orchestration-validering i maskinrommet (oppgave 24.1)
Ny node_kind 'orchestration' med strukturert metadata-validering:
- trigger.event valideres mot kjent liste (node.created, edge.created,
  communication.ended, node.published, scheduled.due, manual)
- trigger.conditions må være objekt hvis satt
- executor valideres mot script/bot/dream
- intelligence og effort valideres som heltall 1-3
- compiled valideres som boolean
- pipeline valideres som array

Valideringen kjører i både create_node og update_node, identisk
mønster som validate_collection_traits og validate_ai_preset_metadata.

Ref: docs/concepts/orkestrering.md
2026-03-18 16:43:11 +00:00
1e34c3c67a Valider fase 15–16: sikkerhet, konsistens og atomisk toggle
Tre fikser funnet under validering:

1. SIKKERHET: Admin-endepunkter manglet autorisasjonssjekk.
   Alle /admin/*-endepunkter brukte kun AuthUser (autentisert),
   ikke admin-rolle. Ny AdminUser-extractor sjekker owner/admin-edge
   til samling — returnerer 403 Forbidden for ikke-admins.
   Berører: maintenance, jobs, resources, health, ai, usage.

2. Race condition i toggle_effect: les-modifiser-skriv uten transaksjon
   på active_effects JSON. Erstattet med atomisk PG jsonb-operasjon.

3. Manglende updated_by i set_gain, set_mute, set_mixer_role, toggle_effect.
   Nå spores hvem som endret mixer-tilstanden.
2026-03-18 15:39:30 +00:00
e25b5a11ef Valider fase 13–14: fiks size-inkonsistens, sikkerhet og konsistens i traits + publisering
Feil funnet og fikset:

1. truncate() UTF-8-panikkfeil: Brukte byte-indeksering s[..max] som
   panikker på flerbyte-tegn (æ, ø, å). Byttet til char_indices().

2. VALID_TRAITS manglet 'mixer' og 'ai_tool': Dokumentert i
   docs/primitiver/traits.md men avvist av backend-validering.

3. Planlagte artikler synlige før publish_at: serve_article og alle
   listevisninger (forside, arkiv, kategori, søk) eksponerte artikler
   med fremtidig publish_at. Lagt til tidsfiltere i alle spørringer.

4. A/B klikk-attribusjon logget alle varianter: serve_article logget
   klikk for ALLE aktive varianter ved direkte artikkelbesøk, ikke
   bare den viste. Fjernet feilaktig attribusjon — klikk logges kun
   via track_click-endepunktet med spesifikk variant-parameter.

5. JSON-LD XSS via </script>: serde_json escaper ikke </script>-
   sekvenser, så brukertitler kunne bryte ut av <script>-blokken.
   Lagt til .replace("</", "<\\/") etter serialisering.

6. Hardkodet farge i search.html: Brukte rgba(233,69,96,0.1) i stedet
   for tema-variabel. Byttet til color-mix() med --color-accent.

Nye tester: truncate med UTF-8, truncate kort streng, JSON-LD XSS-escape.
2026-03-18 15:27:38 +00:00
a836be6992 Valider fase 11: fiks size-inkonsistens og UTF-8-trunkering i RSS
Fase 11 (produksjon) validert — LiveKit, pruning og podcast-RSS:

- rss.rs + synops-rss: Les filstørrelse fra både 'size_bytes' (intentions)
  og 'size' (publishing) med COALESCE — forhindrer manglende enclosure-
  størrelse i podcast-feeds avhengig av opplastingsmetode.
- pruning.rs + synops-prune: Samme COALESCE-fix for konsistent size-tracking.
- rss.rs + synops-rss: Fiks truncate_description til å bruke char-indeksering
  istedenfor byte-indeksering — forhindrer panic på norsk tekst (å, ø, æ).

LiveKit kjører i Docker (healthy), token-generering via join_communication,
pruning-loop aktiv, RSS-endepunkt returnerer korrekt 404 for ukjent slug.
Alle 61 maskinrommet-tester bestått.
2026-03-18 15:15:08 +00:00
382f93132f Valider fase 9–10: fiks typefeil, konsistens og feilhåndtering
Fase 9 (visninger):
- graph: fiks TypeScript-feil (string|undefined → string|null)
- kanban board: les kolonner fra metadata.traits.kanban.columns
  (konsistent med KanbanTrait), behold fallback til metadata.columns
- dagbok: fiks createdAt-type (number, ikke BigInt med microsSinceUnixEpoch)

Fase 10 (AI):
- summarize.rs: refaktorer til cli_dispatch helper (DRY, konsistent
  med ai_edges.rs og tts.rs)
- ai_process.rs: feil tidlig hvis LITELLM_MASTER_KEY mangler
  (var unwrap_or_default → tom streng → forvirrende 401)

Alle 61 maskinrommet-tester bestått, alle CLI-verktøy kompilerer.
LiteLLM kjører med riktig modellruting (rutine/resonering).
2026-03-18 15:09:03 +00:00
5dfaeff53c Valider fase 3–4: fiks belongs_to-tilgangspropagering og mottakssortering
Validering av fase 3 (frontend) og fase 4 (tilgangskontroll) avdekket to bugs:

1. belongs_to-access-gap: Når en belongs_to-edge opprettes ETTER at
   noen allerede har tilgang til foreldrenoden, fikk ikke barnenoden
   tilgangsoppføringer i node_access-matrisen. F.eks. kunne Vegard (eier
   av en kommunikasjonsnode) ikke se innhold opprettet av Claude med
   belongs_to-edge til den noden.

   Løsning: Ny PG-funksjon propagate_belongs_to_access() som kopierer
   forelderens tilgangsrader til barnet. Kalles fra maskinrommet ved
   opprettelse av belongs_to-edges (create_node m/context, create_edge,
   create_communication m/context). Retroaktiv fiks for eksisterende data.

2. Mottaksflate-sortering: Brukte .microsSinceUnixEpoch (SpacetimeDB-
   BigInt-arv) på vanlig number-felt, ga alltid 0n → ingen sortering.
   Fikset til direkte number-sammenligning.

Verifisert: SvelteKit + maskinrommet bygger og kjører. PG-skjema, OIDC,
WebSocket/NOTIFY, RLS-policies, team-transitivitet og visibility fungerer.
2026-03-18 14:41:20 +00:00
6bb1665b30 Validering 23.1: fase 1–2 (infra + maskinrommet) verifisert
Systematisk gjennomgang av PG-skjema, auth-middleware, intensjoner,
skrivestien og WebSocket-laget. Alle kjernetabeller matcher docs.
Auth fungerer korrekt (401 for ugyldig/manglende token). Skrivestien
er konsistent: direkte PG-skriving → NOTIFY → WebSocket.

Fikser:
- Fjern død kode: pg_writes enqueue-funksjoner (aldri kalt etter STDB-migrering)
- Fjern ubrukt truncate() i tts.rs
- Legg til #[allow(dead_code)] for sqlx-structs med ubrukte felt
- Rett feilaktig doc-påstand i api_grensesnitt.md om jobbkø
- Fjern utdatert STDB-referanse i agent_api.md
- Kompilerer uten warnings

Se logs/validering-23.1.md for fullstendig rapport.
2026-03-18 13:58:50 +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
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
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
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
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
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
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
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
d4fc271e1e Implementer source_material edge-validering (oppgave 20.2)
Legger til maskinrommet-validering for source_material edges i både
create_edge og update_edge. Metadata må inneholde:
- context: "quoted", "summarized" eller "referenced"
- excerpt: ikke-tom streng med kildeteksten

Oppdaterer edges.md med dokumentasjon av metadata-formatet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:04:02 +00:00
8bf82a78d9 Implementer message_placements (oppgave 20.1)
Plasseringsrelasjon som sporer hvor meldinger vises på tvers av
kontekster (chat, kanban, storyboard, kalender, notes). Grunnmuren
for universell overføring mellom verktøy-paneler.

Tre deler:
- PG-migrasjon 016: message_placements tabell med UNIQUE constraint
  og indekser for kontekst- og meldingsoppslag
- SpacetimeDB: MessagePlacement tabell + place_message, remove_placement,
  move_on_canvas reducers for sanntids UI-oppdatering
- Maskinrommet: STDB-klientmetoder for de tre reducerne

Avvik fra spec: FK refererer nodes(id) i stedet for messages(id) siden
meldinger er noder (node_kind = 'melding'). Spec oppdatert tilsvarende.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:59:07 +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
776bc895c1 Fullfører oppgave 18.3: Direction-logikk for AI-prosessering
Implementerer de to retningene for AI-verktøyet:

- tool_to_node ("Penselen"): Lagrer original content som revisjon i
  ny node_revisions-tabell, deretter oppdaterer noden med AI-output
  i både STDB (sanntid) og PG (persistering).

- node_to_tool ("Kverna"): Oppretter ny node med AI-output, med
  derived_from-edge tilbake til kildenoden og processed_by-edge
  til AI-preseten. Full sporbarhet i grafen.

Ny PG-tabell: node_revisions (node_id, content, title, metadata,
revision_type, created_by, ai_preset_id, job_id).

Ref: docs/features/ai_verktoy.md § 2.2, § 6.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:40:26 +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