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.
This commit is contained in:
parent
e0d06ddff4
commit
6bb1665b30
8 changed files with 120 additions and 134 deletions
|
|
@ -112,7 +112,7 @@ med `job_type: 'spec_update'`. Oppretter `revision`-node med
|
|||
forrige versjon for historikk.
|
||||
|
||||
#### `POST /agent/respond`
|
||||
Send et svar i en samtale (erstatter dagens direkte STDB-skriving).
|
||||
Send et svar i en samtale.
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ Skriver til PG `mixer_channels`-tabell; NOTIFY-trigger propagerer til WS.
|
|||
- Intensjoner fra frontend → `POST /intentions/*` endepunkter i maskinrommet.
|
||||
- Tunge spørringer fra frontend → `GET /query/*` endepunkter i maskinrommet.
|
||||
- Frontend (SvelteKit) har ingen direkte databasetilgang.
|
||||
- Bakgrunnsjobber trigges av maskinrommet, ikke via en separat jobbkø-tabell.
|
||||
- Bakgrunnsjobber trigges av maskinrommet via `job_queue`-tabellen (se `docs/infra/jobbkø.md`).
|
||||
|
||||
> **Historisk merknad:** Tidligere beskrev dette dokumentet en arkitektur
|
||||
> der SvelteKit var web-API og Rust kun var workers. Denne ble erstattet
|
||||
|
|
|
|||
105
logs/validering-23.1.md
Normal file
105
logs/validering-23.1.md
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Validering 23.1 — Fase 1–2 (infra + maskinrommet)
|
||||
|
||||
Dato: 2026-03-18
|
||||
|
||||
## PG-skjema vs. docs
|
||||
|
||||
### nodes-tabell
|
||||
- **Match:** Alle kolonner fra `docs/primitiver/nodes.md` finnes i PG med korrekte typer og defaults.
|
||||
- **Ekstra kolonner fra senere faser:** `last_accessed_at` (migrasjon 010, pruning), `search_vector` (migrasjon 011, fulltekstsøk). Disse er dokumentert i sine respektive migrasjoner.
|
||||
- **Indekser:** Alle tre dokumenterte indekser (`idx_nodes_kind`, `idx_nodes_created_by`, `idx_nodes_visibility`) er på plass. I tillegg 4 ytelsesindekser fra migrasjon 017.
|
||||
- **Triggere:** `nodes_notify` (PG NOTIFY), `trg_nodes_search_vector` (fulltekstsøk). Begge korrekte.
|
||||
- **RLS:** `node_select`-policy korrekt — sjekker created_by, aliaser, node_access, og visibility.
|
||||
|
||||
### edges-tabell
|
||||
- **Match:** Eksakt match med `docs/primitiver/edges.md` — alle kolonner, constraints, defaults.
|
||||
- **UNIQUE constraint:** `(source_id, target_id, edge_type)` korrekt.
|
||||
- **Indekser:** Alle tre dokumenterte + 3 ekstra ytelsesindekser. OK.
|
||||
- **Trigger:** `edges_notify` for PG NOTIFY. Korrekt.
|
||||
- **RLS:** `edge_select`-policy sjekker system-flagg, created_by, aliaser, og node_access.
|
||||
|
||||
### node_access-tabell
|
||||
- **Match:** Eksakt match med migrasjon 001. `(subject_id, object_id)` PK, `via_edge` med CASCADE.
|
||||
- **CASCADE-oppbygning:** Når en tilgangsgivende edge slettes, fjernes tilhørende `node_access`-rader automatisk via `ON DELETE CASCADE` på `via_edge`. Elegant og korrekt.
|
||||
- **Trigger:** `node_access_notify` for sanntidsoppdatering av klientenes tilgangsmatrise.
|
||||
|
||||
### auth_identities-tabell
|
||||
- **Match:** Eksakt match. `node_id` PK, `authentik_sub` UNIQUE, `email` UNIQUE.
|
||||
- **Seed-data:** Vegard er opprettet med korrekt node_id og authentik_sub.
|
||||
|
||||
### Enums
|
||||
- `visibility`: hidden, discoverable, readable, open — matcher docs.
|
||||
- `access_level`: reader, member, admin, owner — matcher docs.
|
||||
- `job_status`: pending, running, completed, error, retry — matcher jobbkø-spec.
|
||||
|
||||
### recompute_access-funksjon
|
||||
- Korrekt implementert i migrasjon 001. Håndterer direkte tilgang, transitiv belongs_to, og team-propagering.
|
||||
|
||||
## Auth-middleware
|
||||
|
||||
- **JWKS:** Hentes fra Authentik ved oppstart. Støtter kid-matching.
|
||||
- **JWT-validering:** RS256, issuer og audience valideres. Korrekt.
|
||||
- **Identitetsoppslag:** `auth_identities.authentik_sub` → `node_id`. Fungerer.
|
||||
- **401-respons:** Verifisert med curl — returnerer 401 for manglende token og ugyldig token.
|
||||
- **WebSocket:** Bruker samme JWT-validering via query-parameter `?token=<JWT>`.
|
||||
|
||||
## Skrivestien (intensjoner)
|
||||
|
||||
### create_node
|
||||
- Direkte PG-skriving med NOTIFY-trigger. Kontekst-arv (automatisk belongs_to-edge) fungerer.
|
||||
- Alias-oppløsning for kontekstbasert identitet (fase 8.2).
|
||||
- Agent-trigger og AI edge-forslag spawnes asynkront. Korrekt.
|
||||
- Validering: visibility-enum, traits for samlingsnoder, AI-preset-metadata.
|
||||
|
||||
### create_edge
|
||||
- Direkte PG-skriving. For tilgangsgivende edges (owner/admin/member_of/reader): transaksjon med recompute_access.
|
||||
- Publiseringsvalidering: submitted_to og belongs_to til require_approval-samlinger.
|
||||
- source_material-validering: context + excerpt påkrevd.
|
||||
- A/B-test-trigger for presentasjonselement-edges.
|
||||
|
||||
### update_node / delete_node
|
||||
- Tilgangskontroll: `user_can_modify_node` sjekker created_by, owner/admin-edge, og alias.
|
||||
- Partial update: kun oppgitte felter endres, resten beholdes fra eksisterende node.
|
||||
- Re-rendering trigges ved custom_domain- eller temaendring.
|
||||
|
||||
### update_edge / delete_edge
|
||||
- Tilgangskontroll: `user_can_modify_edge` sjekker created_by, owner/admin til source-noden, og alias.
|
||||
- submitted_to status-endring krever owner/admin av samlingen.
|
||||
- Cache-invalidering ved belongs_to-sletting.
|
||||
|
||||
## WebSocket / sanntid
|
||||
|
||||
- PG LISTEN/NOTIFY-triggere: `node_changed`, `edge_changed`, `access_changed`, `mixer_channel_changed`.
|
||||
- Berikelse: full raddata hentes fra PG etter NOTIFY (ikke bare ID).
|
||||
- Tilgangsfiltrering: `should_send_to_user` sjekker `visible_nodes` (HashSet).
|
||||
- Initial sync: alle noder, edges, access og mixer_channels brukeren kan se.
|
||||
- Resync ved lag: sender full initial_sync.
|
||||
- `visible_nodes` oppdateres dynamisk ved access_changed-events.
|
||||
|
||||
## Jobbkø
|
||||
|
||||
- PostgreSQL-basert: `SELECT ... FOR UPDATE SKIP LOCKED`.
|
||||
- Retry med eksponentiell backoff (30s × 2^n).
|
||||
- Ressursstyring: semaphore (3 samtidige), CPU-vekt, LiveKit-bevisst governor.
|
||||
- Vedlikeholdsmodus: pauser dequeue.
|
||||
- CLI-dispatch: spawner `synops-*`-verktøy for tunge jobber.
|
||||
|
||||
## Funn og fikser
|
||||
|
||||
### Fjernet død kode
|
||||
1. **pg_writes.rs: enqueue_*-funksjoner** — Aldri kalt etter STDB-migreringen. Intensjonene skriver nå direkte til PG. Funksjoner fjernet, kommentar lagt til.
|
||||
2. **tts.rs: truncate()** — Ubrukt hjelpefunksjon. Fjernet.
|
||||
3. **ai_process.rs: SourceNodeRow** — `#[allow(dead_code)]` lagt til (struct-felt brukes av sqlx men ikke lest direkte i kode).
|
||||
4. **intentions.rs: FullEdgeRow** — `#[allow(dead_code)]` lagt til (samme mønster).
|
||||
|
||||
### Oppdaterte docs
|
||||
1. **docs/infra/api_grensesnitt.md** — Rettet feilaktig påstand om at bakgrunnsjobber ikke bruker jobbkø-tabell.
|
||||
2. **docs/infra/agent_api.md** — Fjernet utdatert STDB-referanse.
|
||||
|
||||
### Kompilering
|
||||
- Maskinrommet kompilerer uten warnings etter fiks.
|
||||
- Maskinrommet kjører og svarer på `/health` med `{ "status": "ok" }`.
|
||||
|
||||
## Konklusjon
|
||||
|
||||
Fase 1–2 er solid implementert. PG-skjema matcher docs eksakt for kjernetabellene. Auth-middleware fungerer korrekt. Skrivestien er konsistent: direkte PG-skriving → NOTIFY → WebSocket. Ingen funksjonelle feil funnet. Kun død kode og småjusteringer i docs.
|
||||
|
|
@ -29,6 +29,7 @@ use crate::jobs::JobRow;
|
|||
use crate::resource_usage;
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
#[allow(dead_code)]
|
||||
struct SourceNodeRow {
|
||||
content: Option<String>,
|
||||
title: Option<String>,
|
||||
|
|
|
|||
|
|
@ -1388,6 +1388,7 @@ pub struct DeleteEdgeResponse {
|
|||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
#[allow(dead_code)]
|
||||
struct FullEdgeRow {
|
||||
source_id: Uuid,
|
||||
target_id: Uuid,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// pg_writes — Jobbkø-handlere for PG-skriveoperasjoner.
|
||||
//
|
||||
// Erstatter fire-and-forget `tokio::spawn()` med retry via jobbkøen.
|
||||
// Hver skriveoperasjon (insert/update/delete for nodes og edges) er en
|
||||
// egen jobbtype som behandles av den eksisterende worker-loopen med
|
||||
// eksponentiell backoff (30s × 2^n) og dead letter queue (status='error'
|
||||
// etter max_attempts).
|
||||
// Historisk kontekst: Disse handlene ble opprettet da skrivestien gikk
|
||||
// via jobbkøen (STDB → async PG-skriving). Etter STDB-migreringen (fase 22)
|
||||
// skriver intensjonene direkte til PG, og NOTIFY-triggere sender
|
||||
// sanntidsoppdateringer. Handlene beholdes for å prosessere eventuelle
|
||||
// gjenværende jobber i køen.
|
||||
//
|
||||
// Jobbtyper:
|
||||
// pg_insert_node, pg_insert_edge, pg_update_node,
|
||||
|
|
@ -19,122 +19,11 @@ use uuid::Uuid;
|
|||
use crate::jobs::JobRow;
|
||||
use crate::publishing::IndexCache;
|
||||
|
||||
/// Prioritet for PG-skriveoperasjoner. Høy — data-konsistens er kritisk.
|
||||
const PG_WRITE_PRIORITY: i16 = 8;
|
||||
|
||||
// =============================================================================
|
||||
// Enqueue-funksjoner (erstatter spawn_pg_*)
|
||||
// =============================================================================
|
||||
|
||||
/// Legger en insert_node-operasjon i jobbkøen.
|
||||
pub fn enqueue_insert_node(
|
||||
db: PgPool,
|
||||
node_id: Uuid,
|
||||
node_kind: String,
|
||||
title: String,
|
||||
content: String,
|
||||
visibility: String,
|
||||
metadata: serde_json::Value,
|
||||
created_by: Uuid,
|
||||
) {
|
||||
let payload = json!({
|
||||
"node_id": node_id,
|
||||
"node_kind": node_kind,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"visibility": visibility,
|
||||
"metadata": metadata,
|
||||
"created_by": created_by,
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::jobs::enqueue(&db, "pg_insert_node", payload, None, PG_WRITE_PRIORITY).await {
|
||||
tracing::error!(node_id = %node_id, error = %e, "Kunne ikke legge pg_insert_node i jobbkø");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Legger en insert_edge-operasjon i jobbkøen.
|
||||
pub fn enqueue_insert_edge(
|
||||
db: PgPool,
|
||||
edge_id: Uuid,
|
||||
source_id: Uuid,
|
||||
target_id: Uuid,
|
||||
edge_type: String,
|
||||
metadata: serde_json::Value,
|
||||
system: bool,
|
||||
created_by: Uuid,
|
||||
) {
|
||||
let payload = json!({
|
||||
"edge_id": edge_id,
|
||||
"source_id": source_id,
|
||||
"target_id": target_id,
|
||||
"edge_type": edge_type,
|
||||
"metadata": metadata,
|
||||
"system": system,
|
||||
"created_by": created_by,
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::jobs::enqueue(&db, "pg_insert_edge", payload, None, PG_WRITE_PRIORITY).await {
|
||||
tracing::error!(edge_id = %edge_id, error = %e, "Kunne ikke legge pg_insert_edge i jobbkø");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Legger en update_node-operasjon i jobbkøen.
|
||||
pub fn enqueue_update_node(
|
||||
db: PgPool,
|
||||
node_id: Uuid,
|
||||
node_kind: String,
|
||||
title: String,
|
||||
content: String,
|
||||
visibility: String,
|
||||
metadata: serde_json::Value,
|
||||
) {
|
||||
let payload = json!({
|
||||
"node_id": node_id,
|
||||
"node_kind": node_kind,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"visibility": visibility,
|
||||
"metadata": metadata,
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::jobs::enqueue(&db, "pg_update_node", payload, None, PG_WRITE_PRIORITY).await {
|
||||
tracing::error!(node_id = %node_id, error = %e, "Kunne ikke legge pg_update_node i jobbkø");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Legger en delete_node-operasjon i jobbkøen.
|
||||
pub fn enqueue_delete_node(db: PgPool, node_id: Uuid) {
|
||||
let payload = json!({ "node_id": node_id });
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::jobs::enqueue(&db, "pg_delete_node", payload, None, PG_WRITE_PRIORITY).await {
|
||||
tracing::error!(node_id = %node_id, error = %e, "Kunne ikke legge pg_delete_node i jobbkø");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Legger en delete_edge-operasjon i jobbkøen.
|
||||
pub fn enqueue_delete_edge(
|
||||
db: PgPool,
|
||||
edge_id: Uuid,
|
||||
source_id: Uuid,
|
||||
target_id: Uuid,
|
||||
edge_type: String,
|
||||
) {
|
||||
let payload = json!({
|
||||
"edge_id": edge_id,
|
||||
"source_id": source_id,
|
||||
"target_id": target_id,
|
||||
"edge_type": edge_type,
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = crate::jobs::enqueue(&db, "pg_delete_edge", payload, None, PG_WRITE_PRIORITY).await {
|
||||
tracing::error!(edge_id = %edge_id, error = %e, "Kunne ikke legge pg_delete_edge i jobbkø");
|
||||
}
|
||||
});
|
||||
}
|
||||
// Enqueue-funksjonene (enqueue_insert_node, enqueue_insert_edge, etc.) er fjernet.
|
||||
// Etter STDB-migreringen (fase 22) skriver intensjonene direkte til PG.
|
||||
// NOTIFY-triggere sender sanntidsoppdateringer via WebSocket.
|
||||
// Handle-funksjonene under beholdes for å prosessere eventuelle gjenværende
|
||||
// jobber i køen fra den gamle arkitekturen.
|
||||
|
||||
// =============================================================================
|
||||
// Job-handlere (kalles fra dispatch i jobs.rs)
|
||||
|
|
|
|||
|
|
@ -135,12 +135,3 @@ async fn resolve_voice_id(
|
|||
.unwrap_or_else(|_| "21m00Tcm4TlvDq8ikWAM".to_string()))
|
||||
}
|
||||
|
||||
/// Forkorter en streng til maks `max_len` tegn med "..." suffix.
|
||||
fn truncate(s: &str, max_len: usize) -> String {
|
||||
if s.len() <= max_len {
|
||||
s.to_string()
|
||||
} else {
|
||||
let end = s.char_indices().nth(max_len - 3).map(|(i, _)| i).unwrap_or(s.len());
|
||||
format!("{}...", &s[..end])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
tasks.md
3
tasks.md
|
|
@ -295,8 +295,7 @@ verifiserer mot spec, og sjekker at ting faktisk fungerer. Ved funn:
|
|||
fiks direkte hvis det er småting, eller opprett nye work_items (tasks)
|
||||
med spesifikasjon for det som trenger en dedikert sesjon.
|
||||
|
||||
- [~] 23.1 Valider fase 1–2 (infra + maskinrommet): PG-skjema, indekser, auth-middleware, intensjoner, STDB-klient (nå erstattet av WS). Verifiser at skjema matcher docs, at auth fungerer, at skrivestien er konsistent.
|
||||
> Påbegynt: 2026-03-18T13:50
|
||||
- [x] 23.1 Valider fase 1–2 (infra + maskinrommet): PG-skjema, indekser, auth-middleware, intensjoner, STDB-klient (nå erstattet av WS). Verifiser at skjema matcher docs, at auth fungerer, at skrivestien er konsistent.
|
||||
- [ ] 23.2 Valider fase 3–4 (frontend + tilgang): SvelteKit-oppsett, OIDC-flow, sanntid, mottaksflate, TipTap-editor, node_access-matrise, team-transitivitet, visibility-filtrering.
|
||||
- [ ] 23.3 Valider fase 5–8 (kommunikasjon + CAS + lyd + aliaser): chat-loop, kontekst-arv, CAS-hashing/deduplisering, Whisper-pipeline, segmenttabell, SRT-eksport, alias-identitet.
|
||||
- [ ] 23.4 Valider fase 9–10 (visninger + AI): kanban drag-and-drop, kalender, dagbok, kunnskapsgraf, LiteLLM-ruting, AI-foreslåtte edges, oppsummering, TTS.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue