d3cae27fa4e0f510bd8f404561d019f0992f0d9a
15 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d3cae27fa4 |
Bilagor ut ur händelseloggen — referens och hash i stället för inbäddat
Foton, video och instrumentbilder låg som data-URL:er inne i händelserna. Det drabbade allt som läser loggen: synken drog med hela bildmassan var femtonde sekund, kundvyn likaså, och en säkerhetskopia av loggen var i praktiken en kopia av alla foton. Nu ligger innehållet utanför händelsen och loggen bär en referens med innehållets SHA-256. Det försvagar inte bevisvärdet utan stärker det: hashen står i den append-only-skyddade loggen, så en bild som bytts ut går att upptäcka. Tidigare låg bilden i loggen och måste helt enkelt tros på. Innehållet kontrolleras mot hashen varje gång det lämnas ut — stämmer det inte svarar tjänsten 409 i stället för att visa bilden. Innehållsadresserat, så samma foto som dokumenteras två gånger lagras en gång. Två lägen: databas (bytea, fungerar överallt utan konfiguration) och s3 (AWS, MinIO, Ceph). Signeringen är egen — SigV4 för PUT och GET — i stället för molnleverantörens SDK, eftersom två operationer inte motiverar tiotals megabyte beroenden i en bild som annars bara har pg-drivrutinen. Den korsverifieras bit för bit mot botocore i testerna. Det avslöjade en riktig bugg direkt: host-huvudet saknade portnummer, vilket hade fungerat mot AWS men avvisats av all självhostad S3. Kodningen av objektnycklar kanoniseras medvetet inte. S3 följer andra URL-regler än övriga AWS-tjänster och en felgissad regel ger signaturer som ser rimliga ut men avvisas. I stället begränsas hink och prefix till tecken som aldrig behöver kodas — då finns ingen regel att gissa fel på. Delningsgränsen gäller även bilagor: en bilaga lämnas bara ut via en delningslänk om händelsen den hör till är synlig på den nivån, så den skannade arbetsordern nås aldrig via kundlänken. Uppladdningen sker på ett enda ställe — sidans egna skicka() flyttar innehållet innan händelsen skrivs, så ingen panel behövde ändras. Misslyckas det, eller saknas server som i lokalt läge, bäddas det in precis som förut. Dokumentationen får aldrig gå förlorad för att nätet ligger nere, och äldre händelser med inbäddad data-URL fortsätter fungera för alltid eftersom loggen är append-only. Verifierat: 96 vitest-tester (varav 9 nya för signering och innehållsadressering), typkontroll, eslint, OpenAPI-validering, terraform fmt och referenskontroll, samt integrationstest mot riktig Postgres med 13 nya kontroller — bland annat att ett manipulerat innehåll upptäcks och inte lämnas ut, och att kundlänken når fotot men inte arbetsordern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
a7574015b1 |
Åtkomsthärdning: kontospärr, omedelbar återkallelse, takt på inloggning
Två luckor från genomgången, båda i auth. En utfärdad token gällde sina tolv timmar ut. Det fanns dessutom inget sätt att stänga av ett konto alls — en person som slutade behöll åtkomsten till organisationens ärenden. Nu bär token en version, och varje autentiserat anrop slår upp kontot och kontrollerar att det är aktivt och att versionen stämmer. Det kostar ett uppslag på primärnyckeln per anrop och ger i gengäld omedelbar verkan i stället för en avstängning som börjar gälla någon gång i morgon. Administratören stänger av och öppnar konton i användarlistan. Att stänga av höjer token-versionen, så pågående sessioner upphör direkt; öppnas kontot igen förblir de gamla token döda. Ingen kan stänga av sig själv och organisationsgränsen gäller. Var och en kan dessutom logga ut på alla enheter — vägen ut när en telefon tappats bort. Händelseloggen rörs aldrig: historiken är fortfarande knuten till den som utförde arbetet. Lösenord kunde gissas i obegränsad takt. Spärren ligger nu i databasen, inte i minnet, så den håller bakom flera repliker: 10 misslyckade försök per konto och 30 per källadress inom 15 minuter. Spärren gäller kontot även vid rätt lösenord — annars kunde den kringgås av den som till slut gissar rätt. Andra konton påverkas inte. Inget lösenord lagras, bara att ett försök skedde och om det lyckades, och rader äldre än ett dygn städas bort i skrivvägen. Ett avstängt konto räknas som misslyckat försök så att svarstiden inte avslöjar vilka konton som finns. Verifierat: 87 vitest-tester, typkontroll, eslint, OpenAPI-validering och integrationstest mot riktig Postgres med 13 nya kontroller — rollstyrning, självavstängning, organisationsgränsen, att en utfärdad token dör direkt, att den förblir död efter återöppning, logga-ut-alla samt att spärren slår till, gäller även rätt lösenord och inte smittar andra konton. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
19796bec70 |
Terraform blir enda vägen, och backup ett obligatoriskt val
Kustomize- och Argo CD-vägen är borttagen. Den beskrev samma system en
gång till och kunde inte köras samtidigt som Terraform utan att de
motarbetade varandra — selfHeal återställde det Terraform ändrade och
prune tog bort det Terraform skapade. Kvar utanför Terraform är bara
postgres-init.sql, som läses av både definitionen och integrationstestet
så att schemat inte kan glida isär från det som testas.
Säkerhetskopieringen var en förhoppning: en StatefulSet med en volym och
ingen kopia. Går volymen förlorad är det inte "data" som försvinner utan
varje ärendes bevisvärde — vad som kontrollerades, av vem, när, med
vilken evidens — och det går inte att återskapa i efterhand.
databas_lage är därför ett obligatoriskt val utan standardvärde:
extern managerad Postgres, leverantörens backup och PITR
(rekommenderat i produktion)
cnpg CloudNativePG i klustret: basbackup 02:30, kontinuerlig
WAL-arkivering till objektlagring, PITR och failover
inbyggd en volym, ingen backup — spärras av en precondition när
miljön är produktion
Preconditions fångar felkonfiguration vid plan i stället för vid drift:
extern utan anslutning, cnpg utan backupmål eller nycklar, inbyggd i
produktion.
Driftsättningen är nu två åtskilda flöden. Publicera bygger och taggar
bilderna vid varje main-push; Driftsätt startas för hand med en tagg mot
en GitHub-miljö som kan kräva godkännande, kör fmt/init/validate/plan/
apply, skriver ut kartan och rökkontrollerar hälsa och API-spec. En bild
i registret är inte samma sak som en bild som kör. Rollback är att köra
Driftsätt igen med en tidigare tagg. CI kör dessutom terraform validate
på varje PR — den kontroll jag inte kunde köra själv.
Två fel hittade vid egengranskning av definitionen: schemafilen delades
på semikolon, vilket hade klippt itu plpgsql-funktionen med
append-only-triggern (nu hela filen via postInitApplicationSQLRefs), och
null-satta fält i kubernetes_manifest utelämnas nu i stället.
Verifierat: 87 vitest-tester, typkontroll, eslint, OpenAPI-validering,
terraform fmt, statisk referenskontroll av modulen och integrationstest
mot riktig Postgres. terraform validate kunde inte köras här —
registry.terraform.io är blockerad av sessionens egress-policy, därav
CI-jobbet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
cbc7bf2751 |
Genomgång av flöden och infrastruktur — infrakarta i Terraform
Infrastrukturen får en definition som går att läsa: infra/terraform, där karta.tf beskriver hela systemet en gång som data — tjänster, portar, routing, vilken tjänst som ser vilken hemlighet, dataflöden och gränser. Resten av filerna läser därifrån i stället för att upprepa namn och portar, och `terraform output karta` skriver ut samma innehåll i klartext direkt ur definitionen. Filerna är numrerade i läsordning. Genomgången hittade sex saker som är åtgärdade här: Uppslaget mot märkesspecifika kopplingar kunde riktas inåt. Bas-URL:en sätts av kundens administratör men anropet görs av vår server — 169.254.169.254 eller ett internt tjänstenamn hade nått molnets metadatatjänst respektive klustrets insida, och svaret kommit tillbaka mappat genom svarsfälten. En tenant-administratör är inte infrastrukturens ägare. Nu stoppas IP-literaler, namn som resolvar till privata adresser och .local/.internal innan något anrop görs, och nätverkspolicyn undantar samma nät. TILLAT_INTERNA_UPPSLAG öppnar för verkstäder som har OEM-servern på eget nät. Delningsfiltret var en nekalista, alltså blev varje ny händelsetyp automatiskt synlig i kundens delningslänk tills någon kom ihåg att neka den — fel håll att fela åt på en integritetsgräns. Nu räknas i stället upp vad som får delas per nivå, och ett test kräver att varje händelsetyp i domänmodellen är klassificerad. Samma ändring i Supabase-funktionen via ny migration. Beteendet i dag är oförändrat; det är riktningen som vänts. Ingressen saknade kroppsgräns och hade därmed nginx standard på 1 MB medan tjänsten tar 4 MB — foto- och videodokumentation hade avvisats i produktion men aldrig i testerna. Satt till 8 MB i båda vägarna. Vidare: nätverkspolicyer som stänger namnrymden och bara öppnar de faktiska flödena, CORS-lista via TILLATNA_URSPRUNG i stället för "*", och säkerhetskontext + startprob på databaspodden. Kustomize-/Argo CD-vägen finns kvar men ska inte köras mot samma kluster som Terraform — selfHeal och prune motarbetar terraform apply. Val och bytesväg dokumenterade. Verifierat: 87 vitest-tester, typkontroll, eslint, OpenAPI-validering, terraform fmt och integrationstest mot riktig Postgres inklusive den nya spärren. terraform validate kunde inte köras här — registry.terraform.io är blockerad av sessionens egress-policy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
3a0be7261e |
Märkesspecifika kopplingar — kunden lägger in sina egna credentials
Verkstaden har redan sina avtal: Volvo-verkstaden har VIDA, VAG-verkstaden har erWin, den fria verkstaden har en fordonsdataleverantör. Kopplingarna konfigureras därför av kunden själv under Inställningar, med sina egna uppgifter — vi tillhandahåller ramen, inte kontot. Uppgifterna når aldrig webbläsaren. De krypteras med AES-256-GCM (INTEGRATION_NYCKEL) innan de skrivs till tabellen integrationer, och API:t returnerar hemliga fält maskerade. Alla uppslag görs av servern. Saknas krypteringsnyckeln sparas ingenting alls — 503 och en förklaring i gränssnittet i stället för klartext i databasen. Endast systemadministratören hanterar uppgifterna; kopplingarna är organisationsknutna som all annan ärendedata. Leverantörer är data, inte kod: URL-mall, autentiseringstyp (bearer/header/basic/query) och svarsmappning beskrivs i services/plattform/integrationer.json, utbytbar via ConfigMap (INTEGRATIONER_FIL). Nya märken läggs till utan att appen byggs om. Varje uppslag skriver senast_testad och senaste_status på kopplingen, så ett utgånget abonnemang syns i inställningarna i stället för att ge tysta tomma svar. Två latenta krascher hittade av klicktestet och åtgärdade: TextFalt och UNDANTAGSORSAKER användes utan import. vite build typkontrollerar inte, så de passerade bygget — därav nya npm-skriptet typkontroll, nu del av verifieringen. Verifierat: 80 vitest-tester, typkontroll, eslint, OpenAPI-validering, integrationstest mot riktig Postgres (rollstyrning, kryptering i vila, maskering, organisationsisolering, fail closed, borttagning) och klickgenomgång mot en körande plattform. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
64b2124cd8 |
Publikt kundgodkännande i delningslänken
Kunden kan nu godkänna eller avböja åtgärdsförslaget direkt i sin Live Share-länk — API:ts enda skrivande publika väg, med sex spärrar som var och en verifieras i integrationstestet: 1. endast delningar på kundnivå (partner-/internlänkar får aldrig svara åt kunden) och aldrig återkallade 2. ärendets ursprungliga delningskod saknar registrerad nivå och kan inte heller svara 3. det måste finnas ett åtgärdsförslag att svara på 4. ett besked per ärende — svaret kan inte ändras i efterhand 5. endast godkant/avbojt/delvis plus kommentar på högst 500 tecken; inget annat kan skrivas till loggen den vägen 6. takt-begränsning per delningskod (429) Beskedet loggas som kundbeslut med kanal "Delningslänk" och avsändaren "Kund via delningslänk"; verkstadens egna registreringar (telefon, på plats …) fungerar precis som förut, och kvalitetsgrindens krav gäller oförändrat. Beslutspanelen visas bara på den publika kundlänken — interna och partnervyer förblir helt skrivskyddade. 73 vitest-tester, integrationstest (åtta nya kontroller för spärrarna) och OpenAPI-validering gröna. Därmed är även den sista dokumenterade avgränsningen utöver externa fordonsdatabaser avklarad. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
09d3558e0c |
Fordonshistorik med orsakskedja, felorsaksstatistik och egen ikongrafik
Fordonshistoriken är nu verklig i stället för självrapporterad:
- GET /api/fordon/{identifierare}/historik: organisationens tidigare
ärenden på samma objekt (regnr/VIN, case-okänsligt) med dokumenterade
felorsaker — organisationsgränsen gäller alltid
- Pre-diagnostikens historiksteg visar tidigare ärenden automatiskt
(servern i inloggat läge, lokala storen offline) och orsakskedjan
kopplas med ett tryck: "kopplat till tidigare ärende #N — …"
- GET /api/statistik/felorsaker (arbetsledare/admin): flottdata —
orsakskategorierna ur alla felorsaksanalyser aggregerade per
organisation, visade som stapelöversikt i arbetsledarvyn
Egen ikongrafik i stället för emojis (src/felsokning/ikoner.tsx):
enkla industriella linjeikoner i SVG (kamera, förstoringsglas, länk,
diagram, kugghjul, bock, kryss, varning, mikrofon, uppdatera, klocka)
och färgpunkter för tillförlitlighets- och statusnivåer. Etiketterna
är ren text ("Hög/Medel/Låg", "Hypotes (ej verifierad)") — maskinellt
verifierat emojifritt i klicktestet.
58 vitest-tester, integrationstest (historik, isolering, statistikens
behörighet) och OpenAPI-validering gröna.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
a37cc027ae |
Personbil-etikett i granskningen + ECM Knowledge Library
Arbetsordergranskningens fältgrupp heter nu Personbil (tidigare Fordon), liksom raden i rapportens fordonsinformation. ECM Knowledge Library: regelbiblioteket är nu serverdistribuerat — regler uppdateras i driften utan appändring, precis som rekommenderat: - Compliance-reglerna är deklarativ data (krav-typ i stället för kod): services/plattform/ecm-regler.json serveras på GET /api/ecm/regler, i klustret utbytbar via ConfigMap (ECM_REGLER_FIL) - Klienten hämtar paketet vid sidladdning, cachar det och faller tillbaka till inbyggt standardpaket offline; trasiga paket och okända krav-typer filtreras (normaliseraRegelpaket) - Kvalitetsgrinden, orsakskategorierna, evidenskällorna och undantagsorsakerna läses ur det aktiva paketet; spårbarhetspaketet bär regelpaketets version - Testat: paritet server/standardpaket, fallback, att ett uppdaterat paket ändrar grinden utan appändring; integrationstestet verifierar serveringen (57 vitest-tester gröna) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
a197ee10ca |
Ärendestart via arbetsorderskanning med konfidensgranskning
Primärvägen när ett ärende startas: teknikern fotar arbetsorderns framsida, orkesterns nya dokumenttolkningsuppgift (Sonnet 5, vision) läser dokumentet layoutoberoende och returnerar strukturerade fält (kund, fordon, verkstad, felbeskrivning) med konfidens per värde. - Konfidensvalidering: ≥95 % godkänns automatiskt, 80–95 % markeras för genomläsning, <80 % kräver aktiv bekräftelse — teknikern granskar bara osäkra fält - Visuell granskning: dokumentet bredvid fälten; klick på ett fält markerar ungefärlig position i bilden - "Starta diagnos" skapar hela ärendet: objekt ur fordonsfälten, metodikval ur felbeskrivningen, tolkningen loggad som ny organisationsintern händelse arbetsorder_skannad (filtreras ur kund- och partnerdelningar i server, RPC och klient) - Schema-bunden vision-uppgift i båda orkestertjänsterna; bild som data-URL, validerad på servern - Inloggade användare tillfrågas aldrig om namn — kontot används - Manuell inmatning kvar som andrahandsväg; tydligt märkt demo-tolkning i lokalt läge - 37 vitest-tester; integrationstestet verifierar filtreringen Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
63b7720159 |
Inställningar: admin väljer objekttyper och identifieringsmetoder
Ny inställningssida (/felsokning/installningar) där systemadmin väljer vad som visas när ett ärende startas. På plattformen sparas valet på organisationen (alla läser via GET /api/organisation, bara admin ändrar via POST /api/organisation/installningar); i lokalt läge gäller valet enheten. Nytt ärende-vyn filtrerar knapparna efter valet. - Klientmodul med normalisering: okända värden filtreras, tomma listor faller tillbaka till standard (går aldrig att låsa ute allt) - installningar-kolumn (jsonb) på organisationer, idempotent migrering - OpenAPI-specen utökad; paritets- och enhetstester - Integrationstest: läsning för alla, 403 för tekniker, org-bred effekt, 400 för tomma listor Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
4161bbf606 |
Ansvarig tekniker och omfördelning från arbetsledarvyn
Ansvarig per ärende härleds ur händelseloggen: skaparen är ansvarig tills en överlämning eller den nya händelsen ansvarig_satt pekar ut någon annan. Arbetsledaren kan omfördela pågående ärenden direkt i översikten; händelsen är organisationsintern och filtreras bort ur kund- och partnerdelningar i klient, plattformstjänst och Supabase-RPC. - Ny händelsetyp ansvarig_satt + projektion ansvarig() (testad) - Briefen visar ansvarig tekniker; översikten visar ansvarig/skapare - Arbetsledare får lista användare (skapa kräver fortfarande admin) - Översikts-SQL:en härleder ansvarig och skapare ur loggen - OpenAPI-specen uppdaterad; integrationstestet utökat (omfördelning, behörigheter, delningsnivåernas filtrering) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
9dbd0aa5f7 |
Live Share-behörighetsnivåer: kund/partner/intern med återkallelse
Modulspecifikationens sista del implementerad:
- Ny tabell delningar: återkallbara länkar med behörighetsnivå
(kund/partner/intern), organisationsknutna via ärendet.
- Nivåstyrd filtrering på serversidan i GET /api/delad/{kod}:
kund utesluter kategoribyten, hypoteser och AI-dialog; partner
utesluter kategoribyten och AI-dialog (hypoteser visas, märkta ej
verifierade); intern visar allt. Ärendets ursprungliga delningskod
fungerar bakåtkompatibelt som kundnivå. Svaret bär nivån.
- Nya endpoints: skapa/lista delningar per ärende och återkalla per
kod — alltid organisationskontrollerat; en återkallad länk ger 404.
- Klienten: delningshanterare i rapportfliken (självhostat läge) med
skapa per nivå, kopiera och återkalla — varje åtgärd loggas i
ärendet; publika delningssidan visar nivåanpassad notis och renderar
serverfiltrerade händelser utan att dölja partner-/interninnehåll.
- OpenAPI-specen utökad (Delning, DelningsNiva, tre nya operationer)
och maskinvaliderad.
Verifierat: integrationstestets 25 kontroller gröna mot Postgres 16 —
inkl. att kundkoden filtrerar hypoteser, partnernivån visar dem men
döljer kategoribyten, internnivån visar allt, org B inte kan skapa
delning av org A:s ärende och att återkallad länk ger 404. 29
vitest-tester gröna, produktionsbygge ok, Playwright-röktest grönt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
12c5f1c617 |
API-first: OpenAPI 3.0-spec för plattforms-API:t
- services/plattform/openapi.yaml dokumenterar hela ytan: auth (registrera organisation, logga in), användarhantering (admin), ärenden + append-only händelselogg (idempotent synk), arbetsledar- översikten, publik Live Share-delning och AI-orkestern — inklusive scheman för alla 15 händelsetyper, roller, JWT-anspråken och API:ts bärande principer (append-only, multi-tenant-404). - Specen serveras live av plattformstjänsten på GET /api/openapi.yaml och följer med i containern. - Verifierad i tre lager: maskinell validering (swagger-cli), paritetstest i vitest (varje dokumenterad väg finns i servern, händelsetyperna är kompletta) och integrationsteststeg som hämtar specen från den körande tjänsten. Verifierat: integrationstestets 18 kontroller gröna mot Postgres 16, 29 vitest-tester gröna, produktionsbygge ok. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
7b7cc855c7 |
Arbetsledarvyn: organisationsöversikt med status och statistik
Rollen arbetsledare får sin vy enligt Master Prompt ('se alla ärenden,
följa status, skapa statistik'):
- Ny behörighetsstyrd endpoint GET /api/oversikt (arbetsledare/admin):
organisationens alla ärenden med status, antal händelser, deltagande
tekniker, objekt och felbeskrivning — allt härlett ur händelseloggen
i en SQL-fråga, aldrig lagrat separat. Tekniker nekas med 403.
- Ny sida /felsokning/oversikt: statistikpanel (totalt/pågående/
avslutade + genomsnittlig ledtid), ärendelista med status och
'Öppna ärendet' som hämtar händelserna till enheten — konfliktfri
flätning om en lokal kopia redan finns. Länkas från kontopanelen
för arbetsledare/admin.
- Integrationstestet utökat: tekniker nekas översikten, arbetsledaren
ser organisationens ärenden och härledningarna (felbeskrivning,
status, händelseantal) stämmer.
Ej implementerat (medvetet): omfördelning av ärenden — överlämnings-
händelsen täcker handover tills en ansvarig-modell införs.
Verifierat: integrationstestets 17 kontroller gröna mot Postgres 16,
28 vitest-tester gröna, produktionsbygge ok, Playwright-röktest grönt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
b069c9a12f |
Organisationslagret: multi-tenant med roller, integrationstestat
Master Prompts tenant- och rollmodell implementerad i den självhostade plattformen: - Registrering skapar organisation + systemadministratör (transaktion); admin skapar övriga användare (tekniker/arbetsledare/admin) i sin organisation via nytt UI och POST /api/anvandare. Roll + organisation ligger i JWT-anspråken och verifieras alltid på servern. - All ärendedata organisationsknuten: ärenden skapas i användarens organisation, händelse-API:t verifierar tillhörighet per anrop och GET /api/arenden listar bara egna organisationens ärenden. - Schemat flyttat till infra/k8s/postgres-init.sql och genereras in som ConfigMap via kustomize — samma fil används av integrationstestet. Nya tabeller: organisationer; anvandare med organisation_id + roll (check-constraint); arenden med organisation_id + index. - Integrationstest (services/plattform/integrationstest.sh) mot riktig Postgres, kört grönt lokalt och tillagt i CI: registrering, synk, idempotens (händelser skrivs aldrig över), append-only-triggern, organisationsisolering (404 över gränsen), publik delning med filtrering, rollstyrning (tekniker nekas användarhantering) och delad arbetsyta inom organisationen. - Klienten: registreringsformulär med organisationsfält, kontopanel med roll och organisation, admin-panel för användarhantering. Verifierat: integrationstestets 12 kontroller gröna mot Postgres 16, 28 vitest-tester gröna, produktionsbygge ok, YAML validerad, Playwright-röktest grönt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |