# Robusthet og fallback

## Lagmodell

```
Lag 1: LLM-leverandør (Claude API, OpenAI, etc.)  → utenfor vår kontroll
Lag 2: LiteLLM (AI Gateway)                        → lokal Docker
Lag 3: Portvokteren                                 → lokal native (systemd)
Lag 4: CLI-verktøy (synops-*)                       → per-kall, isolert
Lag 5: PostgreSQL                                   → lokal Docker, SPOF
```

## Deteksjon

### Bot-nedetid

Portvokteren overvåker `synops-respond` sin exit-kode. Ved feil:

```
@bot (ingen respons på 30 sek)
  → Portvokteren sender automatisk i chatten:
    "⚠ Boten er midlertidig utilgjengelig.
     Meldingen din er lagret og blir besvart
     når tjenesten er tilbake."
  → Oppretter work_item i innboks: "Bot-nedetid" + tagged "bug"
  → Logger i ai_usage_log med error-status
```

Brukeren vet at meldingen er mottatt. Når boten er tilbake,
kan den prosessere ubesvarte meldinger.

### Healthcheck

Portvokteren eksponerer `/health` som sjekker:
- PG-tilkobling
- STDB-tilkobling
- LiteLLM-tilgjengelighet
- Disk-status

Systemd restarter portvokteren ved gjentatte feil.

## LLM fallback-kjede

LiteLLM håndterer modell-fallback. `@bot` fungerer uansett
hvilken modell som svarer:

```
synops-respond → LiteLLM
                   ├── Claude (primær)
                   ├── GPT-4 (fallback 1)
                   ├── Gemini (fallback 2)
                   └── Lokal modell (fallback 3, alltid oppe)
```

Brukeren merker kanskje kvalitetsforskjell, men tjenesten er
oppe. Fallback-kjeden konfigureres i LiteLLM — ingen endring
i portvokteren eller synops-respond.

## Ekstern API-bot og CLI-verktøy

En LLM via API har ikke shell-tilgang. Den trenger det heller
ikke — portvokteren er mellomleddet.

### Function calling / tool use

```
Bruker: "@bot sjekk om RSS-feeden oppdaterte seg"
  → portvokteren → synops-respond → LLM API

LLM svarer med structured output:
  {
    "response": "Jeg sjekker RSS-feeden for deg.",
    "actions": [
      { "tool": "synops-rss",
        "args": { "collection_id": "abc123" } }
    ]
  }

Portvokteren:
  1. Poster chat-svaret i samtalen
  2. Spawner: synops-rss --payload-json '{"collection_id":"abc123"}'
  3. Poster resultatet som oppfølgingsmelding
```

LLM-en beskriver *hva* som skal gjøres. Portvokteren *gjør*
det. Navnekonvensjonen (`synops-{tool}`) betyr at portvokteren
kan dispatche uten hardkodet mapping — samme generiske dispatch
som jobbkøen (se `docs/retninger/unix_filosofi.md`).

### Tilgangskontroll for actions

Ikke alle brukere skal kunne trigge alle verktøy via `@bot`.
Portvokteren sjekker:

1. Brukerens rolle i konteksten (owner/admin/member/reader)
2. Verktøyets tilgangsnivå (fra `cli_tool`-nodens metadata)
3. Rate limiting per bruker

En `reader` kan spørre `@bot` om informasjon, men ikke trigge
`synops-render` eller `synops-audio`.

## Fallback per situasjon

| Situasjon | Deteksjon | Fallback | Brukeropplevelse |
|-----------|-----------|----------|------------------|
| Claude API nede | LiteLLM timeout | Neste modell i kjeden | Svarer, kanskje litt dårligere |
| Alle LLM-er nede | synops-respond exit != 0 | Statisk "utilgjengelig" + work_item | Vet at meldingen er mottatt |
| Portvokteren nede | Systemd healthcheck → restart | CLI fungerer for Claude Code | Web-brukere venter, terminal funker |
| PG nede | Connection refused | Alt stopper | Eneste reelle SPOF |
| STDB nede | Reconnect-loop | PG-fallback for lesing, skriving bufres | Sanntid borte, data trygt |

## PG som eneste SPOF

PostgreSQL er den eneste komponenten der nedetid stopper alt.
Men det er også den mest stabile — PG krasjer sjelden, og
gjenoppbygging fra backup er veldokumentert
(se `docs/setup/produksjon.md`).

Mitigering:
- Automatisk PG-dump (se oppgave 12.2)
- STDB kan gjenoppbygges fra PG
- `synops-snapshot` sikrer at docs-fallback finnes i repo
- CAS-filer er uavhengig av PG (filsystem)

## Onboarding som statisk dispatch

Første `@bot` i en ny brukers velkomst-chat trenger ikke
LLM-roundtrip. Portvokteren gjenkjenner mønsteret og serverer
onboarding-noden direkte:

```rust
if is_welcome_chat && is_first_bot_mention {
    return serve_static_node(ONBOARDING_NODE_UUID);
}
```

Null latency, null AI-kostnad, fungerer selv om alle LLM-er
er nede. Onboarding-teksten er en vanlig node — oppdaterbar
uten redeploy.

Etter første melding fungerer chatten normalt med LLM.

## Bygger på
- `docs/infra/ai_gateway.md` — LiteLLM fallback-kjeder
- `docs/retninger/unix_filosofi.md` — generisk dispatch
- `docs/retninger/maskinrommet.md` — portvokter-rollen
- `docs/concepts/arbeidstavlen.md` — @bot-konvensjonen
- `docs/concepts/selvdokumenterende_system.md` — onboarding-node
