To spor for agent-respons:
1. Permanent: agent er member_of → svar på alle meldinger
2. @bot-nevnelse: bruker skriver @bot → engangs-svar, agent
trenger ikke være deltaker
Femkompis-problemet løst: @bot i en gruppechat gir ett svar,
ikke evig bot-loop. Dedikerte bot-chatter (member_of) fungerer
som før.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tre moduser i agent_identities.handler_mode:
- internal: maskinrommet kjører synops-respond (eksternt API)
- external: jobb settes til 'deferred', forblir urørt for Claude Code
- paused: svar bruker med "AI utilgjengelig", marker done
Jobbkøen overskriver ikke deferred-status (sjekker result.status).
Ny job_status 'deferred' i PG enum.
Scripts: vaktmester-poll.sh (finn deferred jobber),
vaktmester-complete.sh (marker behandlet).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WebSocket-filtrering brukte statisk visible_nodes-cache som ikke
inneholdt nye noder. For INSERT-events sjekkes nå node_access i PG
direkte, og noden legges til i cachen. Løser timing-problemet der
node_changed kom før access_changed.
chat-reply.sh: skriv melding som Claude Code med access-propagering.
agent.rs: jobb opprettes uansett kill-switch, deferred for ekstern handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fjern X-Frame-Options fra Forgejo og Authentik, erstatt med
Content-Security-Policy: frame-ancestors 'self' *.synops.no.
Web Viewer kan nå vise Git og Auth inne i arbeidsflaten.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tre nye node-typer for oppgavestyring:
- proposal: status (draft/discussed/approved/rejected/parked)
- assignment: status (open/planning/active/paused/done/blocked) + priority
- task: status (open/active/done/failed/skipped) + priority
Validering i create_node og update_node. Ingen ny tabell —
bruker eksisterende nodes-tabell med metadata.
Ref: docs/infra/oppgaver.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ny «Nivåer»-fane i /admin/ai med synops/low, medium, high, extreme
- Per-nivå: fallback-kjede, provider-administrasjon, kostnadsestimat
- Test-knapp sender prompt gjennom LiteLLM og viser respons, latens, tokens, kostnad
- Backend: POST /admin/ai/test_prompt + GET /admin/ai/tier_costs
- Migration 033: oppretter de fire synops/* aliasene med providers
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
Ny admin-side som viser registrerte AI-agenter med status, token-forbruk,
aktive jobber og kjørehistorikk. Støtter kill switch for å aktivere/deaktivere.
WorkspaceRow.metadata var String, men PG-kolonnen er JSONB.
Første opprettelse fungerte (inserter Value), men oppslag
feilet ved andre besøk.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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)
Når redirect_feed er satt i podcast-trait, returnerer maskinrommet
HTTP 301 Moved Permanently med Location-header i stedet for å serve
feeden. iTunes new-feed-url-taggen bevares også i RSS-en for klienter
som ikke følger 301.
Admin-UI: erstatter det enkle tekstfeltet med tre tilstander:
- Inaktiv: knapp "Flytt podcast til annen plattform..."
- Bekreftelse: advarsel + URL-felt + rød "Aktiver redirect"-knapp
- Aktiv: gul statusindikator med deaktiver-knapp
Backend: sjekker redirect_feed tidlig i generate_feed() og returnerer
301 før noe annet arbeid gjøres (DB-oppslag for episodes osv).
- Inkluder samlinger med rss-trait (ikke bare podcast-trait) i dropdown
- Fiks slug-lesing fra traits.publishing.slug
- Installer synops-import-podcast til /usr/local/bin
- Marker oppgave 30.7 som ferdig i tasks.md
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
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.
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.
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
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).
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
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.
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
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.
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
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
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
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
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.
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.
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
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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).
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.
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.
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.
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