Commit graph

13 commits

Author SHA1 Message Date
f81c8a96e0 Fullfør oppgave 8.2: Kontekstbasert identitet med alias
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>
2026-03-17 19:19:36 +01:00
89abd5eee4 Legg til create_alias-endepunkt og alias-spørring
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>
2026-03-17 19:00:08 +01:00
b4ee80a97b Fullfør oppgave 7.7: Re-transkripsjonsflyt med side-om-side-sammenligning
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>
2026-03-17 18:41:09 +01:00
0967e43af8 Implementer transkripsjonsvisning med segmenter (oppgave 7.6)
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>
2026-03-17 18:29:43 +01:00
9768a24693 Fullfør oppgave 7.2: Transkripsjons-pipeline (CAS → Whisper → content)
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>
2026-03-17 17:44:54 +01:00
924ac1b6d0 Fullfør oppgave 6.2: Upload-endepunkt for mediefiler
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>
2026-03-17 16:46:54 +01:00
b55705d12c Kontekst-arv: automatisk belongs_to-edge ved input i kommunikasjonsnode
Legger til context_id-parameter på create_node-intensjonen. Når context_id
er satt (og peker på en kommunikasjonsnode), opprettes automatisk en
belongs_to-edge fra den nye noden til kontekstnoden. Dette er kjernen i
kontekst-arv: si noe i et møte → noden tilhører møtet automatisk.

Backend: Validerer at context_id eksisterer og er communication-node,
oppretter belongs_to-edge i STDB+PG etter node-opprettelse.
Frontend: Oppdaterer API-typer med context_id og belongs_to_edge_id.

Ref: docs/retninger/universell_input.md (kontekst arves automatisk)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:57:25 +01:00
7189925d08 Fullfør oppgave 5.1: create_communication-intensjon
Ny intensjon POST /intentions/create_communication som oppretter en
kommunikasjonsnode (node_kind='communication') med:
- metadata.started_at satt til opprettelsestidspunkt
- owner-edge fra innlogget bruker til noden
- member_of-edges for alle angitte deltakere
- Validering av deltaker-noder og visibility
- Samme to-lags skriveflyt som andre intensjoner (STDB instant, PG async)

Testet med curl mot produksjonsserver — node, edges og node_access
opprettes korrekt i både STDB og PG.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:44:02 +01:00
1355d189b2 Fullfør oppgave 4.4: RLS-policies på PG med node_access-filtrering
Implementerer Row Level Security for tunge PostgreSQL-spørringer.
Maskinrommet skriver som superuser (sidelinja), men leser med
SET LOCAL ROLE synops_reader som er underlagt RLS-policies.

Endringer:
- Migration 004: synops_reader rolle, current_node_id() funksjon,
  RLS-policies på nodes (created_by/node_access/visibility),
  edges (endepunkt-tilgang + system-edge-skjuling),
  og node_access (kun egne rader)
- queries.rs: RLS-kontekst-helper (set_rls_context) og
  GET /query/nodes endepunkt med søk, filtrering og paginering
- migration_safety.md: omskrevet fra v1 workspace-RLS til
  node_access-basert RLS med oppdaterte leak hunter-tester

Verifisert på server: hidden noder filtrert for ukjente brukere,
synlige for eiere. Edges filtrert tilsvarende.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:30:29 +01:00
8fa2849f0c Legg til node_access i STDB + synk fra maskinrommet
Visibility-filtrering (oppgave 4.3, del 1/2):
- Ny node_access-tabell i STDB-modulen som speiler PG
- Reducers: upsert_node_access, delete_node_access, delete_node_access_for_subject
- STDB-klient i maskinrommet: metoder for node_access
- Warmup synker node_access fra PG til STDB ved oppstart
- Tilgangsgivende edges synker node_access til STDB etter PG-commit
- clear_all tømmer også node_access

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 15:09:55 +01:00
c20fc9149b Fullfør oppgave 4.1: recompute_access ved edge-endring
Når en tilgangsgivende edge (owner, admin, member_of, reader)
opprettes, kalles nå recompute_access() i samme PG-transaksjon
som edge-insertet. Dette sikrer at node_access-matrisen alltid
er oppdatert — ingen vindu med stale tilgang.

Implementasjon:
- edge_type_to_access_level() mapper edge-typer til access_level enum
- insert_edge_with_access() wrapper edge-insert + recompute_access i tx
- Vanlige edges (belongs_to, mentions, etc.) skrives som før (fire-and-forget)

Verifisert med SQL-tester: direkte tilgang + transitiv tilgang via
belongs_to-edges fungerer korrekt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 14:51:30 +01:00
1e9a4c83c3 Flere intensjoner: create_edge, update_node, delete_node (oppgave 2.5)
Implementerer tre nye skrivestier i maskinrommet med tilgangskontroll:

- POST /intentions/create_edge — opprett retningsbestemt edge mellom
  to noder. Validerer at begge noder eksisterer og edge_type er satt.
- POST /intentions/update_node — partial update av eksisterende node.
  Kun oppgitte felter endres, resten beholdes fra PG.
- POST /intentions/delete_node — slett node med cascade av edges.

Tilgangskontroll for update/delete: brukeren må enten være created_by
på noden, eller ha en owner/admin-edge til den. Sjekkes mot PG som
autoritativ kilde.

Alle endepunkter følger samme mønster som create_node:
STDB-skriving (instant) → async PG-persistering → umiddelbar respons.

Verifisert på server med 10 testcaser:
1. /me med gyldig token → 200
2. create_node → 200 med node_id
3. create_edge (gyldig) → 200 med edge_id
4. create_edge (ugyldig source) → 400
5. create_edge (tom edge_type) → 400
6. update_node (partial, eier) → 200
7. update_node (ingen tilgang) → 403
8. delete_node (eier) → 200
9. delete_node (ingen tilgang) → 403
10. update via owner-edge (Sidelinja) → 200

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:25:16 +01:00
61b35d3c01 Skrivestien: POST /intentions/create_node (oppgave 2.4)
Implementerer den første intensjonen i maskinrommet — skrivestien
som gjør at frontend kan opprette noder via maskinrommet.

Flyten:
1. Valider input (node_kind, visibility, metadata)
2. Generer UUIDv7 (tidssortert)
3. Skriv til SpacetimeDB (instant — frontend ser noden umiddelbart)
4. Spawn async tokio-task for PG-persistering
5. Returner node_id uten å vente på PG

Verifisert på server med fire testcaser:
1. Uten auth → 401
2. Ugyldig visibility → 400 med feilmelding
3. Minimal request (tomt body) → 200, node opprettet med defaults
4. Full request → 200, node verifisert i både STDB og PG

Også: Dockerfile oppdatert til Rust 1.88 (avhengigheter krevde >1.86),
og api_grensesnitt.md oppdatert med endepunktdokumentasjon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:09:50 +01:00