Files
alva/felsokning/docs/SYSTEM-DESCRIPTION.sv.md
T
Claude 9035dfebc9 Engelska är nu systembeskrivningens källa
Filerna byter namn så att basnamnet är den engelska versionen:
SYSTEM-DESCRIPTION.md är dokumentet, övriga språk är suffixade. Bytet
gjordes med git mv så historiken följer med.

Auktoritetskedjan vänds: engelskan säger att den är gällande version och
räknar upp översättningarna; svenska, tyska, danska och norska pekar nu
på engelskan i stället för på svenskan. Två dokument som båda påstår sig
gälla blir i praktiken två sanningar, så exakt ett måste vara källan.

Svenskan blir därmed en översättning bland de andra. Den behåller en
egen not: kodidentifierarna är oöversatta även där, men där syns det
inte eftersom koden är svensk — värt att veta för den som jämför med en
annan språkversion.

Engelskans ingress säger uttryckligen att dokumentets språk inte ändrar
vad koden heter. Det är den missuppfattning som annars uppstår när ett
engelskt dokument beskriver ett svenskt kodbas: läsaren börjar söka på
översatta namn och hittar ingenting.

Alla 48 numrerade avsnitt finns kvar i varje version, verifierat
maskinellt, och inga länkar pekar på de gamla filnamnen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-04 15:15:18 +00:00

39 KiB
Raw Blame History

Guidad Felsökning — fullständig systembeskrivning

Svensk översättning. Källan är SYSTEM-DESCRIPTION.md (engelska). Vid avvikelse gäller det engelska dokumentet.

Kodidentifierare översätts inte — och behöver det inte här: koden är svensk. Det är värt att notera för den som jämför med en annan språkversion, där samma identifierare står oöversatta mitt i främmande text.

Ett självständigt referensdokument. Allt nedan är hämtat ur koden i felsokning/ (branch claude/guidad-felsokning-vision-1mnx7f), inte ur planer eller avsikter. Där något inte finns står det uttryckligen.

Syftet är att kunna resonera om systemet utan tillgång till repot.

Senast synkat mot kod: commit 1bb4031, 2026-08-04.


0. Sammanfattning på trettio sekunder

Guidad Felsökning är en SaaS-plattform för fordonsverkstäder. Den leder en tekniker genom en strukturerad felsökning, kräver bevis för varje påstående, och producerar ett spårbart underlag som kan delas med kund, försäkringsbolag eller nästa tekniker.

Den bärande idén är negativ snarare än positiv: systemet presenterar aldrig en hypotes som ett konstaterat fel. Det är inte en policy i en dokumentfil — det är kodat, testat och blockerar flöden. När evidens saknas står det "Evidens saknas", inte en kvalificerad gissning.

Tekniskt: en append-only händelselogg är enda sanningskällan. Allt annat — ärendevyn, briefen, kundrapporten, kvalitetsgrinden, statistiken — är rena projektioner av loggen och kan alltid regenereras.

Klient React 18 + TypeScript + Vite + Tailwind + zustand + react-router
Backend Två Node-tjänster (plattform, ai-orkester), rena node:http, minimala beroenden
Databas PostgreSQL (Aurora Serverless v2), append-only via databastriggrar
Modell Claude, serverägd routing per uppgift
Infra AWS + EKS, 126 Terraform-resurser i två lager
Git & CI Självhostad Gitea + Actions-runners på egen EKS — inget GitHub i driftvägen
Tester 120 vitest-tester + integrationstest mot riktig Postgres
Språk i kod Svenska (identifierare, kommentarer, commit-meddelanden)

1. Produktprinciper

Dessa fem är inte riktlinjer utan invarianter. Var och en har en motsvarighet i kod och i test.

1.1 Ingen hypotes presenteras som ett konstaterat fel

Hypoteser är en egen händelsetyp (hypotes) med obligatorisk tillförlitlighetsnivå, och kan aldrig anta nivån hog (niva: Exclude<Tillforlitlighet, "hog"> — typsystemet förbjuder det). I kundrapporten märks de uttryckligen som ej verifierade. Kvalitetsgrinden har en egen rad för detta.

Formuleringen vid misslyckad reproduktion är "kunde inte reproduceras under de förhållanden som rådde" — aldrig "felet konstaterat" eller "inget fel hittat". Detta är kodat i både projektionerna och orkesterns grundprompt.

1.2 En kryssruta är inte evidens

Varje kontrollpunkt i varje metodik har ett minimikrav: matvarde | kommentar | foto. En mätning kan inte markeras utförd utan värde; en fotokontroll kan inte markeras utförd utan bild. Vill teknikern hoppa över något krävs ett dokumenterat undantag med orsak ur en fast lista.

Låst av test: "varje kontroll kräver bevis — en kryssruta är inte evidens".

1.3 Loggen är append-only, hela vägen ner

Inga update- eller delete-operationer finns i API:t, och databasen har triggrar som avvisar dem även om någon kringgår applikationen. Ett test söker aktivt efter update/delete mot händelsetabellen i serverkoden och faller om de dyker upp.

Konsekvens: en felaktig uppgift rättas genom en ny händelse, aldrig genom att den gamla försvinner. Historiken är det som ger underlaget värde i en tvist.

1.4 Terminologi

I UI och kundkommunikation används systemet, analysen, bedömningen, beslutsstödet — inte "AI", om det inte är tekniskt nödvändigt. Produkten beskrivs som evidensbaserat diagnossystem / intelligent beslutsstöd.

Skälet är kommersiellt och epistemiskt: en verkstadskund som hör "AI" hör "gissning". En försäkringshandläggare som läser "AI-bedömning" i ett underlag väger det lägre.

1.5 Delningsgränsen är en tillåtelselista

Vad som får lämna organisationen räknas upp positivt, per nivå. En ny händelsetyp är därmed intern tills någon aktivt släpper fram den. Ett test kräver att varje typ i domänmodellen är klassificerad — glöms en bort faller bygget, i stället för att den läcker.


2. Domänmodellen — händelseloggen

app/src/felsokning/domain.ts (281 rader).

Ett ärende är: identitet + metadata + en ordnad lista av loggposter. Varje loggpost bär id, tidpunkt, tekniker och en handelse.

2.1 Samtliga händelsetyper

Typ Innehåll Roll
objekt_identifierat objekt (regnr/VIN, märke, modell, motor …) Vad ärendet gäller
arbetsorder_skannad falt[] + bilaga Tolkad arbetsorder (intern)
felbeskrivning text Kundens ord, ordagrant
arendetyp_satt arendetyp Garanti / försäkring / kund … styr regelpaket
fraga_besvarad stegId, frageId, fraga, svar Metodikens symptomfrågor
kontroll_utford stegId, kontrollId, text, resultat?, undantag? Verifierad checklistpunkt
observation text Vad teknikern såg — inte vad hen tror
matvarde beskrivning, varde, enhet? Mätning (E4)
hypotes text, niva (aldrig hog) Arbetshypotes (intern)
foto beskrivning + bilaga Bildbevis (E2)
video beskrivning + bilaga Rörligt bevis (E3)
matarstallning lage (ingående/utgående), varde + bilaga Miltal in/ut
historik_kontrollerad kontrollerad, kommentar? Servicehistorik
reproducering status (ja/delvis/nej), beskrivning Symptomverifiering
felorsak strukturerad felorsaksanalys Orsak, kategori, underlag
atgardsforslag förslag med motivering Vad som bör göras
kundbeslut godkänt/avböjt, kanal Kundens besked
atgard_utford utfört arbete Vad som faktiskt gjordes
kvalitetskontroll verifiering efter åtgärd Är symptomet borta?
kommentar text Fri anteckning
kategori_byte kategori Tidredovisning (intern)
inaktivitet_forklarad text, minuter Varför det stod stilla
overlamning fran, till? Skiftbyte
ansvarig_satt ansvarig Arbetsledarens omfördelning (intern)
ai_svar rader[] klassificerade, modellnamn Beslutsstödets svar (intern)
export_skapad format, version Exporten loggar sig själv
arende_avslutat signatur? Teknikerns signering

2.2 Bilagor är innehållsadresserade

foto, video, matarstallning och arbetsorder_skannad är intersektionstyper med Bilaga:

export interface Bilaga {
  bilagaId?: string;
  bilagaHash?: string;   // SHA-256
  dataUrl?: string;      // finns kvar för alltid — loggen är append-only
}

Innehållet ligger utanför loggen (S3 eller databas), men hashen ligger i loggen. Vid läsning verifieras hashen; stämmer den inte returneras 409. Innebörden: byter någon ut en bild i lagret upptäcks det, och loggen kan bevisa att den ursprungliga bilden var en annan.

dataUrl behålls i typen därför att gamla poster har den inbäddad — och loggen kan inte skrivas om.


3. Metodikmotorn

Sedan senaste ändringen är motor och innehåll åtskilda:

  • metodik.ts (171 rader) — typer, val av metodik, härledning av nästa steg.
  • metodiker.ts (899 rader) — de sexton metodikerna.

Biblioteket kan växa utan att motorn ändras.

3.1 Metodikbiblioteket

id Namn Område Steg Kontroller
vibration Vibration under körning Hjul och balans symptom → visuell → kontroller → provkorning 19
bromsar Bromssystem Chassi symptom → visuell → matningar → system 14
styrning_fjadring Styrning och fjädring Chassi symptom → visuell → glapp → installning 11
elsystem Elsystem och strömförsörjning El symptom → visuell → matningar → rela → funktionstest 12
start_laddning Start- och laddningssystem El symptom → batteri → start → laddning → krypstrom 15
motor_drift Motorgång och effekt Motor symptom → felkoder → mekanik → tandning_bransle → provkorning 16
kylsystem Kylsystem och överhettning Motor symptom → visuell → matningar → packning 12
drivlina Växellåda och drivlina Drivlina symptom → visuell → matningar → provkorning 10
avgas_emission Avgassystem och emissioner Motor symptom → avlasning → matningar → orsak 12
klimat Klimatanläggning Komfort symptom → visuell → matningar → styrning 11
hogvolt Högvoltsystem — elbil och hybrid Högvolt sakerhet → symptom → avlasning → laddning 16
diagnos_natverk Felkoder och kommunikation Diagnos symptom → grund → buss → koder 10
lackage Läckage Övrigt symptom → visuell → metod 8
missljud Missljud Övrigt symptom → inspelning → lokalisering 7
adas Förarassistans och kalibrering Diagnos symptom → forutsattningar → kalibrering 9
generisk Generell strukturerad felsökning Övrigt symptom → visuell → grundkontroller → funktionstest 9

3.2 Tre regler, låsta av test

  1. Varje kontroll har ett minimikrav. Mätvärde, foto eller observation.
  2. Varje metodik börjar med att verifiera symptomet, aldrig med att åtgärda. Kundens ord blir ett verifierat symptom först när det reproducerats.
  3. Där arbetet kan skada någon ligger säkerhetssteget först. Endast sakerhet får föregå symptom — testet tillåter exakt det undantaget och inget annat.

hogvolt är den enda metodiken med säkerhetssteg. Det kräver behörighet, dokumenterad urtagen servicebrytare (foto), väntetid enligt tillverkaren, mätt spänningsfrihet (mätvärde — inte ett ja på en fråga) och skyddsutrustning. Testet kontrollerar att steget ligger först, att spanningsfrihet kräver mätvärde, och att beskrivningen innehåller ordet "livsfarlig".

Skälet är enkelt: det arbetet kan döda någon. Där duger ingen kryssruta.

3.3 Val av metodik

Tidigare en regexkedja med tre utfall. Nu poängsatt nyckelordsmatchning:

export function metodikPoang(metodik: Metodik, text: string): number
export function valjMetodik(felbeskrivning: string): Metodik
  • Poäng = summan av längden på de nyckelord som träffar. Ett längre — mer specifikt — ord väger tyngre. traktionsbatteri (16) slår batteri.
  • Korta ord (≤3 tecken) matchas som helt ord, längre som ordstam. Annars hade "ac" träffat acceleration och en vibration hamnat i klimatanläggningen.
  • Vid lika poäng vinner den som står först i biblioteket → valet är stabilt mellan körningar.
  • Ingen träff → generisk.

Fallgrop som faktiskt bet under utvecklingen: nyckelorden måste vara stammar, inte färdigböjda ord. Svensk böjning kapar ofta ett e: filter → filtret, så "partikelfilter" matchar aldrig texten teknikern skriver. Samma gäller regenerering → regenererar, misständning → misständer, skrammel → skramlar. Biblioteket använder därför partikelfilt, regenerer, misständ, skram.

Valet är en frågeordning, inte en diagnos. Det avgör var teknikern börjar leta, inte vad som är fel. Träffar inget är generisk det ärliga svaret — strukturellt komplett, och bättre än en gissning.

3.4 Nästa steg

export function nastaSteg(arende: Arende, metodik: Metodik): NastaSteg

Rent härlett ur loggen: första obesvarade frågan, därefter första ej utförda kontrollen, i metodikens ordning. Ingen dold tillståndsmaskin — samma logg ger alltid samma nästa steg.

3.5 Om "täcka allt"

Det går inte att lova ärligt, och dokumentationen påstår det inte. Det som går är att täcka fordonets system systematiskt och låta generisk vara ett strukturellt komplett skyddsnät för det ingen förutsett.


4. ECM v2.0 — evidens- och regelmotorn

app/src/felsokning/ecm.ts (749 rader). Sex motorer:

4.1 Evidence Engine

Evidensnivåer, härledda ur loggen:

Nivå Betydelse
E0 Inget underlag
E1 Teknikerns observation
E2 Foto
E3 Video
E4 Mätvärde
E5 Diagnosdata / dokument
E6 Flera oberoende källor

Ett ärendes evidensnivå är den högsta som underlaget bär. Den visas i UI och följer med i exporten.

Innehållshash: innehallsHash() är en deterministisk FNV-1a över evidensinnehållet. Samma underlag ⇒ samma hash, oavsett maskin eller tidpunkt. Det gör exporten verifierbar i efterhand.

4.2 Rule Engine

  • ORSAKSKATEGORIER — fast lista för felorsaksanalys (ger jämförbar statistik över flottan).
  • UNDANTAGSORSAKER — fast lista för "varför gjordes inte detta".
  • UNDERLAGSKALLOR — vad en slutsats vilar på.
  • INGEN_ATGARD_ORSAKER, KUNDKANALER.
  • granskaAvvikelse() — flaggar text som formulerats som konstaterande utan täckning.

Fasta listor i stället för fritext är ett medvetet val: fritext går inte att aggregera, och statistiken över flottan är en av produktens verkliga tillgångar.

4.3 Compliance Engine

ARENDETYPER styr vilket regelpaket som gäller. Garantiärende kräver claim-nummer och servicehistorik; försäkringsärende kräver skadenummer och bildbevis; kundärende kräver mindre. Paketen är data (ecm-regler.json, serverbart via /api/ecm/regler) — nya krav kräver ingen ny release.

4.4 Validation Engine — pre-diagnostik

Innan felsökningen får börja: objektidentifiering verifierad, arbetsorder inläst, fordonshistorik kontrollerad eller motiverad, ingående mätarställning dokumenterad, kundens felbeskrivning verifierad, tidiga observationer hanterade.

4.5 Completion Engine — kvalitetsgrinden

Den största enskilda funktionen (kvalitetsgrind, ~240 rader). Ärendet kan inte avslutas förrän varje rad är grön eller motiverad:

  • Fordonshistorik kontrollerad eller motiverad
  • Ingående/utgående mätarställning dokumenterad
  • Kundens felbeskrivning verifierad
  • Symptomverifiering: reproducerat, eller dokumenterat ej reproducerbart
  • Felorsaksanalys dokumenterad
  • Åtgärd dokumenterad eller motiverad
  • Kundens besked på åtgärdsförslaget registrerat
  • Utfört arbete trots avböjt åtgärdsförslag (om tillämpligt)
  • Kvalitetskontroll genomförd — symptomet verifierat
  • Metodikens kontroller: evidens eller dokumenterat undantag
  • Foton finns för fotokrävande kontroller
  • Teknikerns slutsats signerad
  • Hypoteser redovisas som ej verifierade
  • Ärendetypens regelpaket uppfyllt (claim / skadenummer / miltal / historik)

4.6 Traceability Engine

sparbarhetspaket() — hela beviskedjan i ett strukturerat objekt: vad som påstås, vad det vilar på, vem som dokumenterade det och när.


5. Symptomverifiering (SVP)

Egen princip därför att den är produktens skarpaste kant mot verkligheten.

Kundens beskrivning ≠ konstaterat fel.

  1. Beskrivningen dokumenteras ordagrant (felbeskrivning).
  2. Den förtydligas via metodikens symptomfrågor — när, var, hur, aldrig "vad är fel".
  3. Den reproduceras, med tre möjliga utfall:
    • Ja — med dokumenterade förhållanden.
    • Delvis — vad som kunde och inte kunde återskapas.
    • Nej — obligatorisk motivering.

Rapportens beviskedja skiljer fyra saker som annars blandas ihop: kundens beskrivning, verifierad observation, felorsaksanalys och rekommenderad åtgärd.


6. Klienten

app/src/felsokning/ + app/src/pages/felsokning/.

Modul Rader Ansvar
ArendeSida.tsx 2433 Ärendevyn. Trekolumnslayout på skrivbord
metodiker.ts 899 Metodikbiblioteket
ecm.ts 749 Regel- och evidensmotorn
NyttArende.tsx 497 Ärendestart, arbetsorderskanning
Arendelista.tsx 399 Dashboard: räknare, filter
projektioner.ts 356 Alla vyer som rena funktioner av loggen
ai.ts 305 Klientsidan av orkestern, promptbygge, svarstolkning
plattform.ts 296 API-klient mot självhostad plattform
DelatArendeVy.tsx 283 Delad vy (kund/partner/intern)
domain.ts 281 Händelsetyper
Installningar.tsx 281 Organisation, användare, integrationer
Oversikt.tsx 238 Arbetsledarvy
demo.ts 200 Demoärende med 1 tim 35 min historik
ui.tsx 174 Industriellt verkstads-UI
metodik.ts 171 Metodikmotorn
synk.ts 141 Konfliktfri ihopflätning av händelser
ikoner.tsx 132 Egna SVG-linjeikoner (inga emojis)
streckkod.ts 131 Streckkods-/VIN-avläsning
store.ts 106 zustand-store
bilagor.ts 96 Uppladdning + blob-URL-cache
installningar.ts 86 Organisationsinställningar
Bilagevisning.tsx 69 <Bild> / <Klipp>
Mikrofon.tsx / rost.ts 66 / 65 Taligenkänning
format.ts 48 Fotoskalning m.m.

6.1 Projektionerna

objekt · felbeskrivning · ansvarig · arendeidentitet · arAvslutat
lokalFordonshistorik · utfordaKontroller · ejKontrollerat
observationer · hypoteser · foton · videor
tidsfordelning · formateraTid · tillforlitlighet
brief · overlamningstext · tidsfordelningsRader · sistaAktivitet

Alla rena funktioner av Arende. ejKontrollerat är den som sparar mest tid i verkligheten: det som orsakar dubbelarbete vid skiftbyte är det ingen skrivit ner att ingen gjort.

6.2 UI-språk

ETKA-inspirerat verkstads-UI: plana ljusgrå ytor (#ECECEC/#F7F7F7), skarpa kanter, djup marinblå primärfärg, tät typografi (1115 px), rektangulära knappar (max 4 px radie), verktygsrad ~44 px. Egna linjeikoner i stället för emojis; status som färgpunkter.

Motivet: teknikern har handskar på sig, står i ett bullrigt utrymme och har inte tid med ett luftigt konsument-UI.

6.3 Lokalt läge

Utan inloggning fungerar appen mot localStorage. Metodiken guidar ensam, orkestern är avstängd. Status visas i ärendehuvudet. Vid inloggning flätas lokala händelser ihop med serverns — konfliktfritt per händelse-id, testat.


7. Backend

7.1 services/plattform (1210 rader)

Ren node:http. Enda beroendet är pg.

API-vägar:

GET  /halsa
GET  /api/openapi.yaml
POST /api/auth/registrera          skapar organisation + systemadministratör
POST /api/auth/logga-in
POST /api/auth/logga-ut-alla       höjer token_version → alla sessioner dör
GET  /api/anvandare                admin
POST /api/anvandare
POST /api/anvandare/{id}/avaktivera | /aktivera
GET  /api/organisation
GET/PUT /api/organisation/installningar
GET  /api/ecm/regler               regelpaket som data
GET/POST /api/arenden
POST /api/arenden/{id}/handelser   append-only
POST /api/arenden/{id}/bilagor
GET  /api/bilagor/{id}             hash verifieras vid läsning
GET  /api/fordon/{identifierare}/historik
GET  /api/statistik/felorsaker
GET  /api/oversikt                 arbetsledarvy
GET  /api/delad/{kod}              filtrerad enligt nivå
POST /api/delad/{kod}/beslut       kundens besked utan inloggning
GET  /api/delad/{kod}/bilagor/{id} nivåfiltrerad
GET  /api/integrationer/leverantorer
GET/PUT/DELETE /api/integrationer/{leverantor}
POST /api/integrationer/{leverantor}/uppslag

Inga update- eller delete-vägar mot ärendedata. Med avsikt.

Säkerhetsfunktioner i tjänsten:

ursprungFor · forTataForsok · kallaFor · inloggningSparrad · loggaForsok
skapaJwt · verifieraJwt · kontoGiltigt · kravAuth · arendeIOrg
integrationsNyckel · kryptera · dekryptera · maskera
arPrivatAdress · pekarInat · gorUppslag · skickaBilaga · synligaTyper

7.2 services/ai-orkester (400 rader)

Äger Claude-nyckeln. Klienten har den aldrig. Routing per uppgift:

Uppgift Modell Effort Vision
handledning claude-sonnet-5 medium
granskning claude-opus-5 high
sammanfattning claude-sonnet-5 low
metodikval claude-haiku-4-5 (ingen — modellen tar inte parametern)
instrumentavlasning claude-sonnet-5 low
dokumenttolkning claude-sonnet-5 low

Samtliga svar är schema-bundna (json_schema). Grundprompten kodar AI-reglerna: "Hitta aldrig på fakta", "aldrig en hypotes som ett konstaterat fel", "KRÄVER verifiering". Vid avböjd förfrågan sker automatisk fallback till reservmodell. Modellen som svarade loggas i varje ai_svar-händelse — underlaget ska gå att granska i efterhand.

Metodikkatalogen byggs ur en enda lista (METODIK_KATALOG), som genererar både schemats enum och promptens punktlista. Ett test jämför den mot klientens bibliotek: glider listorna isär returnerar klassificeraren ett id klienten inte känner igen, och valet skulle falla tyst tillbaka på generisk. Nu faller testet i stället.

7.3 services/gemensam/observation.mjs (166 rader)

Spårning och mätvärden utan nya beroenden. Tjänsterna har medvetet nästan inga beroenden; att dra in ett OpenTelemetry-SDK med trettio paket för att mäta fyra saker vore fel avvägning. I stället två standarder som båda bara är text på stdout:

  • W3C Trace Contexttraceparent följer med genom hela kedjan (klient → plattform → orkester).
  • CloudWatch EMF — strukturerad JSON som CloudWatch själv extraherar mätvärden ur. Ingen agent, ingen SDK, inget som kan sluta fungera tyst.
spårFrån(huvud) · traceparent(spår) · starta(namn, spår)
  → .mät(delnamn, arbete) · .ms() · .delar()
logga(nivå, meddelande, fält) · mätvärde(namn, värde, enhet, dim, extra)
avsluta(spann, { status, väg, extra })

Det som mäts valdes utifrån en fråga: vad vill man veta klockan tre på natten när något är långsamt? Svaret är var tiden gick — inte hur många anrop som skett. Därför delar(): databasen, modellanropet, objektlagringen, kundens leverantör, med antal och summa per del i samma loggrad.

mät() mäter även när arbetet kastar — annars ser fel ut som noll tid.

Dimensioner hålls medvetet få. Varje unik kombination är en egen tidsserie som kostar pengar, så organisation, ärende och spår-id får aldrig bli dimensioner — de ligger som vanliga fält. Låst av test som explicit förbjuder org, organisation, arende, spårId, anvandare bland dimensionerna.


8. Säkerhet

Skydd Implementation
Multi-tenant-isolering Alla ärendefrågor är organisationsknutna (arendeIOrg); integrationstestat mot riktig Postgres
Roller tekniker / arbetsledare / admin, i JWT och databas-check
JWT-anspråk { sub, namn, org, roll, tv }tv = token_version
Omedelbar återkallelse kontoGiltigt() kontrollerar aktiv + token_version vid varje autentiserat anrop. En giltig signatur räcker inte
Global utloggning /api/auth/logga-ut-alla höjer token_version → alla utfärdade tokens dör direkt
Lösenord bcrypt via gen_salt('bf') i databasen
Inloggningsspärr 15 min-fönster; max 10 försök per konto, 30 per källa. Städas probabilistiskt (2 % chans per skrivning) för att slippa ett cron-jobb
Kryptering i vila AES-256-GCM för kundernas integrationsuppgifter; hemliga fält maskeras alltid i API-svar
SSRF-försvar arPrivatAdress() + pekarInat(): 10/8, 127/8, 169.254/16, 172.1631, 192.168/16, 100.64/10, ::1, fc/fd, fe80, ::ffff:. DNS slås upp innan anrop; .local/.internal blockeras. Escape-hatch TILLAT_INTERNA_UPPSLAG för testmiljö
CORS TILLATNA_URSPRUNG-allowlist, ursprung sätts en gång per anrop
Bilageintegritet SHA-256 i loggen, verifieras vid läsning → 409 vid avvikelse
Append-only i databasen Triggrar before update or delete på både felsokning_handelser och felsokning_arenden
Podd-härdning IMDSv2 obligatoriskt, hoppgräns 1 → podar kan inte låna nodens IAM-roll
IRSA Varje tjänstekonto har egen roll; noderna delar inga rättigheter
Delade IAM-roller bygg får publicera till ECR men inte röra klustret; drift får röra klustret men inte publicera bilder
Nätverkspolicy Default deny in; explicita _ut-regler per tjänst
Databasåtkomst Endast från klustrets noder, i ett subnätlager utan routing ut

8.1 Databasschema

organisationer · anvandare · inloggningsforsok
felsokning_arenden · felsokning_handelser
bilagor · bilage_innehall
delningar · integrationer

9. Live Share — delningsnivåer

Tre nivåer, serverstyrd filtrering:

Nivå Ser
kund 22 händelsetyper: objekt, felbeskrivning, frågor, kontroller, observationer, mätvärden, foton, videor, kommentarer, överlämningar, åtgärder, kvalitetskontroll …
partner Allt kunden ser + hypotes (märkta ej verifierade)
intern Full insyn — ingen filtrering

Aldrig utanför organisationen: kategori_byte, hypotes, ai_svar, ansvarig_satt, arbetsorder_skannad.

export function synligaTyper(niva) {
  if (niva === "intern") return null;           // full insyn
  return niva === "partner" ? DELBART_PARTNER : DELBART_KUND;
}

Länkar är återkallbara. Publik delningssida (/felsokning/delad/:kod) kräver ingen inloggning och pollar för liveuppdatering. Kunden kan lämna besked direkt i vyn (POST /api/delad/{kod}/beslut).

Varför tillåtelselista: en neka-lista måste uppdateras när en ny händelsetyp läggs till — och det är precis det man glömmer. En tillåtelselista gör "glömt" till "intern", vilket är det säkra utfallet.


10. Märkesspecifika kopplingar

Leverantörer är data, inte kod (integrationer.json, monterbar som ConfigMap via INTEGRATIONER_FIL). Nya märken kräver ingen ombyggnad.

id Leverantör
generisk_vin Valfri VIN-tjänst över HTTP
vag_erwin Volkswagen Group erWin (VW, Audi, Škoda, SEAT)
volvo_vida Volvo VIDA
fordonsregister Regnr → fordon

Varje leverantör deklarerar sina fält, vilka som är hemliga (krypteras + maskeras), och hur svaret mappas till domänens fält (marke, modell, arsmodell, motor, vaxellada).

Uppslag går genom SSRF-skyddet — en kund kan alltså inte peka en "leverantör" mot klustrets interna adresser.


11. Visual-first

Kameran är integrationslagret. Det som syns på en skärm eller ett instrument fotograferas och tolkas, i stället för att integreras.

  • Arbetsorderskanning är primärvägen vid ärendestart. Sonnet 5 (vision) läser kund-, fordons- och verkstadsuppgifter oavsett layout, med konfidens per fält:

    • 🟢 ≥95 % godkänns automatiskt
    • 🟡 8095 % markeras för genomläsning
    • 🔴 <80 % kräver aktiv bekräftelse

    Teknikern granskar alltså bara osäkra fält. Visuell granskning med dokumentet bredvid fälten; klick markerar ungefärlig position.

  • Instrumentavläsning — foto av diagnosskärm eller instrument → strukturerade värden.

Motivet är kommersiellt: en integration per verkstadssystem är en säljcykel per kund. En kamera fungerar mot allt, direkt.


12. Infrastruktur

Två Terraform-lager. Basen körs sällan, arbetslastlagret ofta.

12.1 infra/aws — basen (91 resurser)

Område Innehåll
Nät 1 VPC, 3 subnätlager × 3 zoner: publikt (bara ALB + NAT), privat (noder, inga publika adresser), data (Aurora, ingen väg ut alls). VPC-endpoints: S3 (gateway); ECR, loggar, Secrets Manager, STS, ELB (gränssnitt) → trafiken lämnar aldrig nätet
Kluster EKS, arm64-noder, IRSA via OIDC-provider, IMDSv2 hoppgräns 1, alla fem kontrollplansloggar på
Data Aurora PostgreSQL Serverless v2, PITR ned till sekunden, KMS med egen nyckel, sslmode=require
Objektlagring S3 för bilagor: SSE-KMS, publik åtkomst blockerad, TLS obligatoriskt, versionshantering på. Plattformens roll får läsa och skriva — men aldrig radera
Register ECR med oföränderliga taggar + sårbarhetsskanning
Hemligheter Secrets Manager; läses endast av plattformsrollen via IRSA
Roller 9 IAM-roller, bl.a. delade bygg / drift
Domän Route 53 + ACM med DNS-validering
Observation 7 larm, 1 instrumentpanel, 3 loggrupper, SNS-topic

Larmen — få, men de som finns betyder något. Ett larm som ingen agerar på lär folk att ignorera larm.

  • Aurora CPU > 85 % i tre perioder (skalningstaket kan vara nått)
  • Aurora fri lokal lagring < 5 GiB
  • Backup-åldertreat_missing_data = "breaching". Saknas mätvärdet finns ingen säkerhetskopiering. En backup man tror finns är värre än ingen.
  • Färre noder än minsta önskade
  • Svarstid p95 > 3 s i tre perioder — inte medelvärde, som döljer att var tjugonde tekniker väntar orimligt länge
  • Serverfel (summa > 5)
  • Modellen avböjer (tyder på oväntat underlag, inte driftfel)

12.2 infra/terraform — arbetslasten (35 resurser)

Läser basen via terraform_remote_state, upprepar ingenting.

kubernetes_namespace_v1        denna, gitea
kubernetes_service_account_v1  plattform (IRSA), drift, runner
kubernetes_deployment_v1       plattform, orkester, web
kubernetes_service_v1          plattform, orkester, web
kubernetes_horizontal_pod_autoscaler_v2  × 3
kubernetes_pod_disruption_budget_v1      × 3
kubernetes_ingress_v1          denna, gitea
kubernetes_network_policy_v1   neka_allt_in, tjanster_in, dns_ut,
                               plattform_ut, orkester_ut, web_ut
kubernetes_manifest            hemlighetskalla, hemligheter (External Secrets)
helm_release                   lastbalanserare (ALB), external_secrets,
                               cloudwatch, metrics, gitea
aws_route53_record             denna

karta.tf (117 rader) producerar en läsbar karta över hela driftbilden: terraform output karta.

12.3 Git och CI — helt egenhostat

Uttrycklig produktbeslut: inget GitHub i driftvägen. Gitea + Actions-runners kör på egen EKS. .gitea/workflows/felsokning.yml:

Jobb Innehåll
test-och-bygg vitest run, typkontroll, eslint, vite build
tjanster eslint på tjänsterna, integrationstest mot riktig Postgres, swagger-cli validate
terraform fmt -check -recursive, init -backend=false, validate
publicera Endast på main, endast om ovanstående gått igenom. Bygger tre bilder, taggar med commit-SHA, pushar till eget ECR. OIDC, ingen statisk nyckel
driftsatt Manuellt (workflow_dispatch) med explicit bildtagg

Driftsättning är ett eget steg med avsikt: en bild i registret är inte samma sak som en bild som kör. Rollback = kör igen med tidigare tagg.

Klientens API-adress bakas in vid bygget (Vite), så bilden är miljöbunden. Byggkontexten för tjänsterna är felsokning/services så att båda når den delade observationsmodulen utan att den dupliceras.


13. Testning

120 vitest-tester i 13 filer:

Fil Antal Låser
ecm.test.ts 32 Evidensnivåer, regelpaket, kvalitetsgrind, pre-diagnostik
metodiker.test.ts 14 Bibliotekets struktur, metodikval, katalogparitet mot orkestern
projektioner.test.ts 13 Vyer som rena funktioner, nästa steg
ai.test.ts 11 Orkesterparitet, OpenAPI ↔ server, append-only, promptregler
observation.test.ts 10 Spårning, EMF-format, förbjudna dimensioner
bilagor.test.ts 9 Innehållshash, SigV4, lagerval
delning.test.ts 7 Tillåtelselistan täcker varje händelsetyp
integrationer.test.ts 7 Leverantörsuppslag, SSRF-guard
demo.test.ts 4 Demoärendet är rikt nog att visa
installningar.test.ts 4 Organisationsinställningar
streckkod.test.ts 4 VIN/streckkod
synk.test.ts 4 Konfliktfri ihopflätning
example.test.ts 1

Utöver enhetstesterna:

  • integrationstest.sh — hela flödet mot riktig Postgres: organisationer, roller, append-only-triggern, isolering, delning, bilagor.
  • SigV4 korsverifierad bit-för-bit mot botocore (sigv4-referens.json).
  • swagger-cli validate på OpenAPI-specen.
  • Paritetstester som jämför spec ↔ server, klient ↔ orkester (× 2 kopior), domänmodell ↔ delningslista.

13.1 Verifieringsloopen före varje commit

npx vitest run                    # 120 tester
npm run typkontroll               # tsc --noEmit  (vite build typkontrollerar INTE)
npx eslint src/felsokning src/pages/felsokning
cd ../services && npx eslint .
npm run build
terraform fmt -check -recursive
# rotens CI: lint · format:check · typecheck · test

typkontroll lades till efter att två latenta krascher (TextFalt och UNDANTAGSORSAKER använda utan import) tagit sig förbi vite build — som transpilerar men inte typkontrollerar.


14. Repostruktur

main är en npm-workspaces-monorepo som heter Semantika och äger roten. När de två produkterna slogs ihop behölls båda, med verktygskedjorna skilda åt per träd — inte genom att försvaga någons regler.

/                             Semantika (workspaces-rot)
├── apps/mobile/              Semantika
├── services/api/             Semantika
├── infra/                    Semantika
├── .github/workflows/ci.yml  Semantika  — rörs inte
│
├── .gitea/workflows/felsokning.yml      Guidad Felsökning (självhostad CI)
└── felsokning/
    ├── app/                  klient (egen package.json, eslint, vitest, tsconfig)
    ├── services/
    │   ├── plattform/
    │   ├── ai-orkester/
    │   └── gemensam/         observation.mjs (delad)
    ├── infra/
    │   ├── aws/              basen, 91 resurser
    │   ├── terraform/        arbetslasten, 35 resurser
    │   └── postgres-init.sql
    ├── docs/
    └── supabase/             migrationer + edge-funktion (äldre driftväg)

Två driftvägar finns parallellt: den självhostade AWS-stacken (den som gäller) och en äldre Supabase-baserad (edge-funktionen felsokning-ai, migrationer). Orkestern finns därför i två kopior som hålls i synk av test.


15. Dokumentation i repot

docs/VISION.md                          produktvisionen
docs/MASTER-PROMPT.md                   grundinstruktionen
docs/MVP.md                             vad som är byggt, funktion för funktion
docs/DEMO.md                            demomanus för visning
docs/DRIFT.md                           drift
docs/SYSTEM-DESCRIPTION.md              engelska — källan
docs/SYSTEM-DESCRIPTION.sv.md           detta dokument
docs/SYSTEM-DESCRIPTION.de.md           tyska
docs/SYSTEM-DESCRIPTION.da.md           danska
docs/SYSTEM-DESCRIPTION.no.md           norska (bokmål)
docs/exempel/vibration-vid-88-km-h.md   genomgående exempelflöde
docs/moduler/
  arbetslogg-och-tidredovisning.md
  arendebrief.md
  evidensmotor.md
  kommunikationsmodell.md
  kundrapport.md
  live-share.md
  markesspecifika-kopplingar.md
  verifierade-checklistor.md

16. Designbeslut och deras motiv

Sammanställda därför att motivet ofta är viktigare än beslutet.

Beslut Motiv
Event sourcing Underlaget måste hålla i en tvist. Historiken är värdet
Append-only även i databasen Applikationslagret kan kringgås; triggern kan det inte
Tillåtelselista för delning Glömd händelsetyp blir intern, inte läckt
Fasta orsakskategorier Fritext går inte att aggregera; flottstatistiken är en tillgång
Serverägd modellrouting Klienten ska aldrig ha nyckeln, och routing ska kunna ändras utan release
Loggad modell per svar Underlaget ska gå att granska i efterhand
Undvik ordet "AI" Kunden hör "gissning"; handläggaren väger det lägre
Visual-first En integration per verkstadssystem = en säljcykel per kund. Kameran fungerar direkt
Leverantörer som data Nytt märke ska inte kräva release
Egen observation, noll beroenden 30 paket för att mäta 4 saker är fel avvägning
Få dimensioner i EMF Varje kombination är en betald tidsserie
p95 i larmet, inte medel Medelvärdet döljer att var tjugonde tekniker väntar
Larm på saknad backupdata En backup man tror finns är värre än ingen
Delade bygg-/driftroller Ett komprometterat bygge ska inte kunna röra klustret
Manuell driftsättning En bild i registret ≠ en bild som kör
Oföränderliga ECR-taggar En tagg ska betyda samma sak imorgon
Innehållsadresserade bilagor Utbytt bild ska upptäckas, inte antas
Hash-verifiering vid läsning Det räcker inte att hasha vid skrivning
S3-rollen får inte radera Append-only måste gälla även lagret
Motor skild från innehåll Biblioteket växer; motorn ska inte behöva ändras
Poängsatt metodikval Regexkedjor blir ogenomskådliga vid 16 alternativ
Nyckelord som stammar Svensk böjning kapar ett e — annars matchar inget
generisk som fallback Ett ärligt "vi vet inte" slår en gissning
Säkerhetssteg först i högvolt Det arbetet kan döda
Svenska i koden Domänen är svensk; översättning fram och tillbaka tappar precision

17. Kända begränsningar och öppna punkter

Uttryckligen inte färdigt:

  • Två orkesterkopior (Supabase edge-funktion + K8s-tjänst) hålls i synk av test, inte av delad kod. Supabase-vägen är den äldre och bör avvecklas.
  • ArendeSida.tsx är 2433 rader. Fungerar, men är den fil som kostar mest att ändra i.
  • terraform validate kan inte köras lokalt i utvecklingsmiljön (utgående nätverkspolicy blockerar providernedladdning). Ersatt av terraform fmt + en egen statisk referenskontroll; den riktiga valideringen sker i CI.
  • Claude-nyckeln fylls i för hand efter första apply — den ligger inte i Terraform-state, med avsikt.
  • postgres-init.sql körs manuellt mot databasen efter basens apply.
  • Ingen automatisk återställningstest av backup. Larmet säger att backup sker, inte att den går att återställa.
  • Metodikbiblioteket täcker inte allt — och påstår det inte. generisk är skyddsnätet.
  • Rotens eslint har 20 förbefintliga fel i Semantikas egna sidor (no-explicit-any) som inte hör till Guidad Felsökning.

18. Ordlista

Svenska Betydelse
Ärende Ett felsökningsuppdrag
Händelse / loggpost Odelbar post i append-only-loggen
Metodik Strukturerat felsökningsflöde
Steg Fas i en metodik (symptom, visuell, mätningar …)
Kontroll Enskild checklistpunkt med minimikrav
Krav matvarde / kommentar / foto
Undantag Dokumenterad orsak till att en kontroll hoppades över
Brief Sammanställd ärendebild — projektion
Kvalitetsgrind Regeluppsättning som måste passeras före avslut
Evidensnivå E0E6, bevisvärdet i underlaget
Reproducering Symptomverifiering: ja / delvis / nej
Felorsak Strukturerad orsaksanalys med kategori och underlag
Delning Extern länk med behörighetsnivå
Orkester Tjänsten som äger modellroutingen
Spann / spår Tidsmätning respektive W3C-spårning
Bilaga Innehållsadresserat foto/video/dokument