Ny rute /calendar som viser alle noder med scheduled-edge i et
månedsbasert kalenderrutenett. Bruker edge-metadata { at: ISO8601 }
for tidspunkt, med T12:00:00-konvensjon for heldagshendelser.
Funksjoner:
- Månedsnavigering med «I dag»-snarvei
- Drag-and-drop for å flytte hendelser mellom datoer (updateEdge)
- Inline-oppretting med tittel og valgfritt klokkeslett
- Fargekoding etter node_kind
- Hendelsesliste under rutenett for gjeldende måned
- Kalender-lenke med hendelsesteller på mottak-siden
- Sanntid via SpacetimeDB (edgeStore.byType('scheduled'))
Arkitekturvalg: Bruker scheduled-edges direkte fra SpacetimeDB
i stedet for legacy calendar_events-tabellen. En node blir en
kalenderoppføring ved å ha en scheduled-edge — konsistent med
«hva edges gjør med noder»-prinsippet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
((completed++)) returnerer 1 når completed=0 under set -e.
Bytter til $((x + 1)) og fanger exitkode med || exit_code=$?.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implementerer kanban som noder+edges uten separate tabeller:
- Board = collection-node med metadata.board og metadata.columns
- Kort = content-noder med belongs_to-edge til board
- Status via status-edge (kort→board) med metadata.value
- Posisjon via belongs_to-edge metadata.position
Backend:
- POST /intentions/update_edge — oppdater edge-type/metadata
- GET /query/board?board_id= — hent kort med status og posisjon
Frontend:
- /board/[id] route med kolonner, drag-and-drop, kortoppretting
- Sanntid via SpacetimeDB edge-subscriptions
- Board-oppretting og navigasjon fra mottak-siden
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Legg til fase 13 (traits), 14 (publisering), 15 (adminpanel) i
avhengighetskartet og blocked_phases-scan
- run-tasks.sh prøver automatisk igjen ved API-feil (500/529) med
lineær backoff (60s, 120s, ...) opptil 10 forsøk
- Skiller mellom bevisst blokkering ([?]/[!]) og krasj — stopper
bare ved blokkering, retries ved krasj
- systemd-service (synops-tasks) for auto-restart ved feil
- Oppdater prompt til å reflektere direkte serverkjøring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Maskinrommet prøver nå opptil 3 ganger med eksponentiell backoff
(2, 4, 8 sek) ved 500/529-feil fra Anthropic. Etter alle forsøk
vises en vennlig melding i chatten i stedet for rå feilmeldinger.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dokumenterer gjeldende modell der egenutviklet kode (maskinrommet,
SvelteKit) kjører native via systemd, mens tredjepartstjenester
(PG, STDB, Authentik, Caddy, Whisper, LiteLLM) kjører i Docker.
- CLAUDE.md: ny driftsmodell-tabell, Claude-agent-seksjon
- docs/arkitektur.md: teknologivalg med kjøremodus-kolonne
- docs/setup/produksjon.md: maskinrommet native instruksjoner
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude er nå en agent-node i grafen som kan delta i samtaler.
Når en bruker sender melding i en kommunikasjonsnode der Claude
er deltaker, enqueues en agent_respond-jobb som kaller claude CLI
direkte og skriver svaret tilbake til chatten.
Nye filer:
- migrations/007_agent_system.sql: agent_identities, agent_permissions, ai_usage_log
- maskinrommet/src/agent.rs: agent_respond job handler
- scripts/maskinrommet.service: systemd-tjeneste for native kjøring
- scripts/maskinrommet-env.sh: genererer env med Docker container-IPs
Endringer:
- intentions.rs: trigger agent_respond ved melding i agent-chat
- jobs.rs: dispatch agent_respond til agent-handler
- frontend chat: bot-badge (🤖) og amber-farge på agent-meldinger
- LiteLLM config: resonering-modellalias via OpenRouter
Maskinrommet kjører nå direkte på hosten (ikke i Docker) for å
ha tilgang til claude CLI. Caddy peker til host.docker.internal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fjerner alle referanser til lokal WSL2-instans og to-instans-modellen.
All utvikling skjer nå direkte på produksjonsserveren via Claude Code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Når en bruker oppretter en node i en kommunikasjonskontekst der
brukerens alias er deltaker (owner/member_of/host_of), settes
created_by til alias-noden i stedet for brukerens hovednode.
Endringer:
- resolve_context_identity(): slår opp brukerens alias i konteksten
- create_node(): bruker alias som created_by når context_id er satt
- user_can_modify_node/edge(): gjenkjenner alias-eierskap ved endring/sletting
- 006_alias_aware_rls.sql: RLS-policies inkluderer alias-opprettede noder
- current_node_alias_ids(): PG-funksjon for alias-oppslag i RLS
Verifisert med integrasjonstest på server:
- Node i alias-kontekst → created_by = alias ✓
- Node uten kontekst → created_by = bruker ✓
- Update/delete av alias-node fungerer for hovedbruker ✓
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementert:
- POST /intentions/create_alias: oppretter person-node og alias-edge
(system=true) fra brukerens hovednode til aliasnoden
- GET /query/aliases: returnerer brukerens alias-noder via alias-edges
- Alias-edgen er usynlig for traversering via eksisterende RLS-policy
som filtrerer system-edges (edges.system = true)
Verifisert med integrasjonstest på server: opprettelse, PG-persistering,
spørring, og at system-edge er korrekt flagget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Del av oppgave 8.1. Endepunktet returnerer alle alias-noder
koblet til brukerens hovednode via alias-edges (system=true).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node.js, Rust og Claude Code installert på produksjonsserveren.
Repo klonet til /home/vegard/synops/, tmux-alias konfigurert.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Oppgave 8.1: Alias-noder med system-edge. Nytt endepunkt
POST /intentions/create_alias oppretter en person-node og
en alias-edge (system=true) fra brukerens hovednode.
GET /query/aliases returnerer brukerens alias-noder.
Alias-edgen er usynlig for traversering via eksisterende
RLS-policy som filtrerer system-edges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nytt GET /query/segments/srt-endepunkt som genererer nedlastbar SRT-fil
fra transcription_segments-tabellen. Bruker RLS-verifisert tilgang.
Frontend har nedlastingsknapp i TranscriptionView med autentisert fetch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ny funksjonalitet for å kjøre re-transkripsjon på eksisterende media-noder
og sammenligne gammel vs ny versjon per segment. Manuelt redigerte segmenter
fra forrige versjon blir uthevet, og brukeren velger per segment hvilken
versjon som skal beholdes.
Backend (Rust):
- POST /intentions/retranscribe — trigger ny Whisper-jobb for media-node
- GET /query/transcription_versions — list alle versjoner for en node
- GET /query/segments_version — hent segmenter for spesifikk versjon
- POST /intentions/resolve_retranscription — anvend per-segment-valg
Frontend (Svelte):
- RetranscriptionCompare.svelte — side-om-side visning med per-segment-valg
- TranscriptionView: re-transkriber-knapp, auto-detect nye versjoner, polling
- API-klient: nye funksjoner for alle re-transkripsjonsendepunkter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend:
- GET /query/segments?node_id=... — henter nyeste segmenter for en media-node
med RLS-basert tilgangssjekk via nodes-tabellen
- POST /intentions/update_segment — redigerer segmenttekst, setter edited=true
Frontend:
- TranscriptionView.svelte: universell komponent for segment-visning med
tidsstempler, avspillingsknapp per segment, og redigerbare tekstfelt
- AudioPlayer: integrert med TranscriptionView når segmenter finnes,
faller tilbake til flat tekst ellers
- Mottak og chat-sider oppdatert med nodeId/accessToken for segment-lasting
- Fikser duration_ms → sekunder-konvertering i metadata-oppslag
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Oppretter transcription_segments-tabellen i PostgreSQL som master-kopi
for alle transkripsjoner. transcribe.rs er oppdatert fra verbose_json
til SRT-format med full parse → segment-innsetting pipeline.
Endringer:
- Migration 005: transcription_segments med GIN fulltekstsøk (norsk)
- transcribe.rs: SRT-parser, segment-innsetting, node-oppdatering
- Miljøvariabler: WHISPER_MODEL (default "medium"), WHISPER_INITIAL_PROMPT
- Docker-compose: nye env vars for maskinrommet-containeren
- Docs: oppdatert podcastfabrikken, arkitektur, primitiver, CLAUDE.md
Tabellen kjørt på server, maskinrommet restartet med nye env vars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Oppretter tabellen som lagrer transkripsjons-segmenter med tidsstempler.
Master-kopi for alle transkripsjoner — SRT og ren tekst er avledede formater.
Inkluderer GIN-indeks for norsk fulltekstsøk og unik constraint per kjøring.
Ref: docs/concepts/podcastfabrikken.md § 3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Legger til AudioPlayer-komponent som spiller av lyd fra CAS-noder
med waveform-visualisering via wavesurfer.js. Komponenten viser
play/pause, tidslinje, og kan ekspandere transkripsjonen.
Chat-visningen inkluderer nå media-noder (has_media-edges) sammen
med tekstmeldinger, sortert kronologisk. Talenotater vises med
mikrofon-ikon, waveform og transkripsjon.
Mottak-siden viser også AudioPlayer for media-noder med lyd.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Legger til VoiceRecorder-komponent som bruker MediaRecorder API for
lydopptak i nettleseren. Opptaket lastes opp til CAS via eksisterende
uploadMedia-endepunkt, som automatisk trigger Whisper-transkripsjon.
Komponenten er integrert i:
- ChatInput: mikrofon-knapp mellom tekstfelt og send-knapp
- NodeEditor: mikrofon-knapp i verktøylinjen
Flyten: opptak → webm/opus blob → upload → CAS → whisper_transcribe-jobb.
Ingen backend-endringer nødvendig — hele transkripsjons-pipelinen fra
oppgave 7.2 gjenbrukes uendret.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer komplett pipeline for automatisk transkripsjon av lydfiler:
- PostgreSQL jobbkø (job_queue-tabell med status, retry, backoff)
- Worker-loop i maskinrommet som poller hvert 2. sekund
- Whisper-integrasjon: leser CAS-fil, sender multipart til faster-whisper API
- Postprosessering: filtrerer hallusinerte segmenter (no_speech_prob > 0.6)
- Oppdaterer media-nodens content-felt med transkripsjon og metadata
- Automatisk trigger: upload_media enqueuer jobb for audio/*-filer
Testet ende-til-ende på server: jobb plukkes opp, Whisper prosesserer,
node oppdateres. Retry med eksponentiell backoff ved feil.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Satt opp faster-whisper-server (fedirz/faster-whisper-server:latest-cpu)
som Docker-tjeneste på produksjonsserveren. Ingen GPU tilgjengelig —
bruker CPU med int8-kvantisering og large-v3 modell for best norsk kvalitet.
Verifisert:
- Transkripsjon fungerer via OpenAI-kompatibelt API
- verbose_json med segmenter og tidskoder OK
- Docker DNS-oppslag fra sidelinja-net fungerer
- Maskinrommet har WHISPER_URL=http://faster-whisper:8000
- RAM-bruk ~2.5 GB med modell lastet
Konfigurasjon:
- Image: fedirz/faster-whisper-server:latest-cpu
- Modell: large-v3 (norsk), int8, CPU
- CAS montert read-only for direkte filtilgang
- Healthcheck via python3 (curl ikke tilgjengelig i image)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Brukeren kan nå dra eller lime inn bilder i TipTap-editoren.
Bildet lastes opp til CAS via upload_media-endepunktet, og settes
inn som <img> med CAS-URL i metadata.document (HTML).
Endringer:
- Ny uploadMedia() og casUrl() i api.ts for multipart upload
- @tiptap/extension-image med CasImage-utvidelse (data-node-id attr)
- handleDrop/handlePaste i editor intercepter bildefiler
- Upload-status vises i editoren mens bilder lastes opp
- accessToken sendes ned til NodeEditor fra +page.svelte
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nytt endepunkt streamer CAS-filer fra disk med riktig Content-Type
(oppslått fra media-nodens metadata i PG) og Cache-Control: immutable.
Hash-validering (64 hex-tegn) hindrer path traversal.
Tokio-streaming for effektiv håndtering av store filer.
Docker-compose oppdatert med CAS-volum for maskinrommet-containeren.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /intentions/upload_media mottar multipart form data, lagrer filen
i CAS med SHA-256 hashing, og oppretter en media-node. Valgfri
source_id oppretter en has_media-edge fra kildenoden til media-noden.
Endepunktet følger etablert skrivestimønster: STDB først (instant),
async PG-persistering i bakgrunnen. Maks filstørrelse 100 MB.
Deduplisering via CAS — identiske filer gir ingen ekstra diskbruk.
Verifisert med curl mot produksjonsserver: upload uten og med
source_id, deduplisering, og PG-persistering fungerer korrekt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implementerer full 1:1 chat-loop:
- NewChatDialog: velg person å starte samtale med
- Fikser API-felt (participant_ids → participants) for korrekt
kommunikasjon med maskinrommets create_communication-endepunkt
- Opprett kommunikasjonsnode med to deltakere (owner + member_of)
- Dedupliserer: finner eksisterende samtale før ny opprettes
- Chat-header viser den andre deltakerens navn i 1:1-samtaler
- Testbruker-node opprettet på server for verifisering
Full loop verifisert via STDB: node + edges + melding + belongs_to
fungerer, WebSocket-subscribers ser endringer i sanntid.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Chat-visning i frontend som viser noder med belongs_to-edge til en
kommunikasjonsnode, sortert på tid, med sanntidsoppdatering via
SpacetimeDB.
Nye filer:
- frontend/src/routes/chat/[id]/+page.svelte — Chat-side som viser
meldinger (noder med belongs_to-edge), deltakere, auto-scroll,
og avsender-info. Bruker edgeStore.byTarget() for reaktive
oppdateringer når nye meldinger kommer via STDB.
- frontend/src/lib/components/ChatInput.svelte — Enkel meldings-input
med Enter-for-send, auto-resize textarea.
Endringer:
- frontend/src/lib/api.ts — Lagt til createCommunication()-funksjon
for å opprette kommunikasjonsnoder fra frontend.
- frontend/src/routes/+page.svelte — Kommunikasjonsnoder i mottaket
er nå klikkbare lenker til chat-visningen. "Ny samtale"-knapp.
- tasks.md — Oppgave 5.3 markert som ferdig.
Arkitektur: Chat-visningen bruker context_id-parameteren i
create_node-intensjonen (implementert i 5.2) for automatisk
belongs_to-edge. Meldinger hentes reaktivt fra STDB-stores —
ingen polling, ingen ekstra API-kall.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>