Nytt konseptdokument for orchestration-noder (⚡):
- Fritekst-instruksjoner som boten tolker og utfører
- Strukturerte triggere (portvokteren evaluerer uten LLM)
- Naturlig progresjon: fritekst → observér → kompilér → DSL
- Kompilerte pipelines med bot-fallback ved feil
- Kaskade mellom orkestreringer via triggers-edges
- Visuell editor med tekstbokser per steg og test-kjøring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.8 KiB
Nodes — spesifikasjon
Status: Besluttet.
Alt er noder. Brukere, team, prosjekter, innhold, møter, mediefiler, kunnskapsgraf-entiteter — alt er rader i
nodes-tabellen. Hva en node "er" bestemmes av dens edges, ikke av noden selv.
Skjema
CREATE TYPE visibility AS ENUM ('hidden', 'discoverable', 'readable', 'open');
CREATE TABLE nodes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
node_kind TEXT NOT NULL DEFAULT 'content',
title TEXT,
content TEXT,
visibility visibility NOT NULL DEFAULT 'hidden',
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_by UUID REFERENCES nodes(id)
);
CREATE INDEX idx_nodes_kind ON nodes (node_kind);
CREATE INDEX idx_nodes_created_by ON nodes (created_by);
CREATE INDEX idx_nodes_visibility ON nodes (visibility);
Kolonner
id
UUID, generert. Primærnøkkel for alt i systemet.
node_kind
Hint om hva noden primært er. Freeform streng — ikke en enum, samme filosofi som edge-typer. Edges kan gi noden flere roller utover dette.
Kjente node_kinds:
| Kind | Eksempel |
|---|---|
person |
Vegard, Trond, Bjørn (alias) |
team |
Podcastteamet |
collection |
Sidelinja Podcast, Research-gruppa |
content |
Chatmelding, bloggpost, notat, dagboknotis |
communication |
Møte, samtale, livesending |
topic |
Kunnskapsgraf-entitet (Jonas Gahr Støre, Skolepolitikk) |
media |
CAS-node (lydfil, bilde, video) |
agent |
AI-agent (Claude, system) |
system_announcement |
Systemvarsler |
ai_preset |
AI-verktøy-preset (prompt, modellprofil, kategori) |
workspace |
Personlig arbeidsflate (én per bruker, auto-provisjonert) |
work_item |
Oppgave, bug, idé, forslag (se docs/concepts/arbeidstavlen.md) |
cli_tool |
CLI-verktøy med spec, bruk og metadata (se docs/retninger/unix_filosofi.md) |
orchestration |
Automatisert oppskrift med trigger og steg (se docs/concepts/orkestrering.md) |
Listen vokser organisk etter behov.
title
Intern visningsnavn brukt i lister, søkeresultater, mottaksflaten. Universelt — navnet på en person, arbeidstittelen på en bloggpost, navnet på en podcast.
- Vegard:
'Vegard' - Sidelinja:
'Sidelinja Podcast' - Bloggpost:
'Hvorfor noder er sentrum' - Chatmelding:
NULL(har sjelden tittel)
Viktig distinksjon: title er det interne displaynavnet. For
publisert innhold er den offentlige overskriften en egen node med
title-edge til artikkelen — fordi den kan ha varianter, A/B-testes,
og har en egen forfatter/tidspunkt. Se
publisering § "Presentasjonselementer".
content
Ren tekst uten formatering. Brukes til fulltekstsøk og enkel
visning. For rike dokumenter (formatert tekst med bilder) genereres
content automatisk fra metadata.document ved lagring.
- Bloggpost: teksten uten markup (generert fra
metadata.document) - Chatmelding:
'Hei, er du klar?' - Voice memo:
NULLved opprettelse, fylles etter transkribering - Brukernode:
NULL - CAS-node:
NULL(binærdata lever på disk)
For formatert innhold: se metadata.document under metadata.
visibility
Default synlighet for alle uten eksplisitt edge. Se noder er sentrum for full spesifikasjon av visibility-nivåer og traverseringsregelen.
metadata
JSONB for alt typespesifikt som ikke er tittel eller ren tekst:
- Person:
{ "display_name": "Vegard", "preferences": { ... } } - CAS-node:
{ "cas_hash": "abc123", "mime": "audio/mp3", "size_bytes": 84000000 } - Kommunikasjonsnode:
{ "started_at": "...", "ended_at": "..." } - Samlings-node:
{ "pruning_profile": "conservative", "theme": "dark" } - Rikt dokument:
{ "document": { "type": "doc", "content": [...] } }
metadata.document inneholder TipTap/ProseMirror JSON for formatert
innhold (overskrifter, bilder, blockquotes, etc.). Bilder refererer
til CAS-noder via node_id. For enkle meldinger (ren tekst) er
document null — content er alt som trengs. Se
universell input for detaljer.
created_at
Tidsstempel, automatisk.
created_by
Referanse til noden som opprettet denne noden. For alias-bruk:
created_by settes til aliasnoden, ikke brukernoden bak.
Brukernoder
En brukernode er en node med node_kind = 'person' og en rad i
auth_identities:
CREATE TABLE auth_identities (
node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
authentik_sub TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL
);
Alt annet — profil, preferanser, relasjoner — er noden og dens edges. Autentiseringstabellen er en tynn bro mellom HTTP-sesjonen og grafen.
CAS-noder (mediefiler)
Binærfiler er noder med node_kind = 'media'. Selve biten lever
på disk i CAS-lageret. Noden bærer bare metadata.
Episode #42 (content)
──has_media──→ CAS-node (media, metadata: { cas_hash: "abc123", mime: "audio/mp3" })
──has_media──→ CAS-node (media, metadata: { cas_hash: "def456", mime: "text/srt" })
──belongs_to──→ Sidelinja Podcast (collection)
En innholdsnode kan ha mange mediefiler via edges. Pruning-logikken i maskinrommet opererer på CAS-noder — sletter binærfilen fra disk, men noden kan leve videre som tombstone.
Eierskap og tilgang
created_by gir redigeringsrett til egne noder. Men sletting og
strukturelle endringer scopes av rollen i konteksten:
- Du kan alltid redigere noder du opprettet.
- Sletting krever
ownerelleradmini samlings-noden innholdet tilhører — selv om du opprettet det. - En privat node (ingen samlings-edge) kan slettes fritt av creator.
Flere noder kan ha owner-tilgang til samme node via
tilgangsmatrisen (direkte og transitiv). Forretningslogikk for
hva ulike tilgangsnivåer tillater lever i maskinrommet, ikke i
databasen.