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.
|
forrige versjon for historikk.
|
||||||
|
|
||||||
#### `POST /agent/respond`
|
#### `POST /agent/respond`
|
||||||
Send et svar i en samtale (erstatter dagens direkte STDB-skriving).
|
Send et svar i en samtale.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ Skriver til PG `mixer_channels`-tabell; NOTIFY-trigger propagerer til WS.
|
||||||
- Intensjoner fra frontend → `POST /intentions/*` endepunkter i maskinrommet.
|
- Intensjoner fra frontend → `POST /intentions/*` endepunkter i maskinrommet.
|
||||||
- Tunge spørringer fra frontend → `GET /query/*` endepunkter i maskinrommet.
|
- Tunge spørringer fra frontend → `GET /query/*` endepunkter i maskinrommet.
|
||||||
- Frontend (SvelteKit) har ingen direkte databasetilgang.
|
- 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
|
> **Historisk merknad:** Tidligere beskrev dette dokumentet en arkitektur
|
||||||
> der SvelteKit var web-API og Rust kun var workers. Denne ble erstattet
|
> 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;
|
use crate::resource_usage;
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
|
#[allow(dead_code)]
|
||||||
struct SourceNodeRow {
|
struct SourceNodeRow {
|
||||||
content: Option<String>,
|
content: Option<String>,
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -1388,6 +1388,7 @@ pub struct DeleteEdgeResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
|
#[allow(dead_code)]
|
||||||
struct FullEdgeRow {
|
struct FullEdgeRow {
|
||||||
source_id: Uuid,
|
source_id: Uuid,
|
||||||
target_id: Uuid,
|
target_id: Uuid,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// pg_writes — Jobbkø-handlere for PG-skriveoperasjoner.
|
// pg_writes — Jobbkø-handlere for PG-skriveoperasjoner.
|
||||||
//
|
//
|
||||||
// Erstatter fire-and-forget `tokio::spawn()` med retry via jobbkøen.
|
// Historisk kontekst: Disse handlene ble opprettet da skrivestien gikk
|
||||||
// Hver skriveoperasjon (insert/update/delete for nodes og edges) er en
|
// via jobbkøen (STDB → async PG-skriving). Etter STDB-migreringen (fase 22)
|
||||||
// egen jobbtype som behandles av den eksisterende worker-loopen med
|
// skriver intensjonene direkte til PG, og NOTIFY-triggere sender
|
||||||
// eksponentiell backoff (30s × 2^n) og dead letter queue (status='error'
|
// sanntidsoppdateringer. Handlene beholdes for å prosessere eventuelle
|
||||||
// etter max_attempts).
|
// gjenværende jobber i køen.
|
||||||
//
|
//
|
||||||
// Jobbtyper:
|
// Jobbtyper:
|
||||||
// pg_insert_node, pg_insert_edge, pg_update_node,
|
// pg_insert_node, pg_insert_edge, pg_update_node,
|
||||||
|
|
@ -19,122 +19,11 @@ use uuid::Uuid;
|
||||||
use crate::jobs::JobRow;
|
use crate::jobs::JobRow;
|
||||||
use crate::publishing::IndexCache;
|
use crate::publishing::IndexCache;
|
||||||
|
|
||||||
/// Prioritet for PG-skriveoperasjoner. Høy — data-konsistens er kritisk.
|
// Enqueue-funksjonene (enqueue_insert_node, enqueue_insert_edge, etc.) er fjernet.
|
||||||
const PG_WRITE_PRIORITY: i16 = 8;
|
// 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
|
||||||
// Enqueue-funksjoner (erstatter spawn_pg_*)
|
// jobber i køen fra den gamle arkitekturen.
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
/// 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ø");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Job-handlere (kalles fra dispatch i jobs.rs)
|
// Job-handlere (kalles fra dispatch i jobs.rs)
|
||||||
|
|
|
||||||
|
|
@ -135,12 +135,3 @@ async fn resolve_voice_id(
|
||||||
.unwrap_or_else(|_| "21m00Tcm4TlvDq8ikWAM".to_string()))
|
.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)
|
fiks direkte hvis det er småting, eller opprett nye work_items (tasks)
|
||||||
med spesifikasjon for det som trenger en dedikert sesjon.
|
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.
|
- [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.
|
||||||
> Påbegynt: 2026-03-18T13:50
|
|
||||||
- [ ] 23.2 Valider fase 3–4 (frontend + tilgang): SvelteKit-oppsett, OIDC-flow, sanntid, mottaksflate, TipTap-editor, node_access-matrise, team-transitivitet, visibility-filtrering.
|
- [ ] 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.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.
|
- [ ] 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