main
640 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
86d553b639 |
Kör ALVA:s tester i CI, och genomgången som incheckat test
CI körde `npm test` i roten, som delegerar till workspaces. Listan är apps/mobile, services/api och infra — felsokning/app står inte där. Pipelinen körde alltså 30 tester från en orelaterad tjänst och inte ett enda av ALVA:s 311. Varken lint eller typkontroll nådde produkten heller. Det är inte att testerna föll. Det är att ingenting upprätthöll dem. Varje garanti som de två revisionerna skrivit som "låst av ett test" — härkomsten, kvalitetsgrinden, raderingen, mätdonsspårbarheten, högvoltsspärren, det stängda schemat, driftskydden mellan klient och grind — låstes av tester som ingen pipeline körde. De höll därför att en person körde dem för hand före varje commit. Det är en person, inte en spärr. Två nya jobb. `alva` kör produktens lint, typkontroll och hela sviten mot sin egen lockfil. `genomgang` installerar Chromium och kör fyra ärenden genom det byggda gränssnittet. Genomgången kontrollerar tre saker: att varje ärende når avslutat läge, att varje händelse klienten faktiskt producerar passerar det stängda schemat, och att interaktionerna per ärende håller sig under 90. Bägge felvägarna är provade, inte antagna — sänkt budget fäller tre fall, och ett schemafilter som avvisar allt fäller fyra. En kontroll som inte kan falla är dekoration. Skriptet bygger appen själv med de två miljövariabler bygget kräver. Lämnas det åt den som startar kommandot testar genomgången tyst en annan applikation: utan hash-routing matchar ingen route, och utan platshållare för den gamla butiksklienten kastar den innan routern monteras så att sidan blir tom. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
31727a4ea0 |
Härda enligt revision 2: stäng schemat, ta bort dubbelregeln
C-5. granskaHändelse itererade schemats nycklar och aldrig händelsens, så okända fält accepterades och sparades ordagrant. Två garantier vilade på motsatsen: krypto-shreddingen skyddar en fast lista av fältnamn, så personuppgifter på en vanlig observation krypterades aldrig och överlevde raderingen — och delningsfiltret är typnivå, så samma fält gick ut i kundens delningslänk. Schemat är nu stängt, med varje valfritt fält deklarerat. Det gäller även `kalla`: protokollinläsningen fungerade bara därför att schemat var öppet. Avslaget är hårt, inte en tyst strykning. Verifierat mot riktig trafik — samtliga händelser som klienten faktiskt producerar passerar. M-7. Klienten upprepar inte längre grindens regel utan anropar grinda() och visar dess egna hinder. Villkoret hade glidit isär två gånger utan att något test märkte det. Det avslöjade omedelbart ett verkligt fel i grinden: den krävde textresultat även på kontroller vars krav är foto — trots att gränssnittet märker fältet "Observation (valfritt)". Servern hade alltså nekat avslut på nästan varje riktigt ärende, och det syntes inte så länge klienten hade ett eget och mildare villkor. Evidens graderas nu efter kontrollens eget krav. M-8. Protokollinläsningen svarar med utfall per händelse i stället för en siffra, 207 vid delvis lyckad inläsning, innehållshärledda id:n i stället för klockan, och en transaktion runt hela importen. M-9. En genererad webhookhemlighet lämnas ut en gång vid skapandet. m-8. Profilens vägslagning begränsas till egna egenskaper. m-10. Ett mätvärde som kommer ur en kontroll redovisas en gång. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
e6b57d59e5 |
Leverantörsrevision 2 av ALVA
Granskar det som byggts sedan revision 1 stängdes: ALVA-metoden och designsystemet, slutsatsen (ALVA-RULE-200), driftmåtten (ALVA-REP-0100), den härledda sammanfattningen (ALVA-PROC-0030) och integrationsytan (ALVA-SPEC-020/021). Ett kritiskt fynd. Händelseschemat är öppet — granskaHändelse itererar schemats nycklar, aldrig händelsens — så okända fält accepteras och sparas ordagrant. Två garantier som revision 1 stängde vilar på motsatsen: personuppgifter på en vanlig observation krypteras aldrig och överlever därför raderingen, och delningsfiltret är typnivå, inte fältnivå, så fälten når kundens delningslänk. Reproducerat mot de delade modulerna, inte påstått. Komplikationen är att öppenheten är bärande: protokollinläsningen hänger `kalla` på varje händelse, och `kalla` finns inte i schemat. Tre allvarliga. Klientens avslutsvillkor har glidit från grinden två gånger sedan C-2 stängdes, och ingen av gångerna fångades av 293 tester — bara av att köra tio ärenden hela vägen. Protokollinläsningen tappar händelser tyst på tre sätt. Genererade webhookhemligheter går aldrig att få ut, så leveranssignaturen kan aldrig verifieras. Tre mindre, varav ett är mitt eget från i dag: ett mätvärde som kommer ur en kontroll redovisas två gånger i rapporten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
45967d086e |
Lås inte in ärenden där åtgärden dokumenterades före förslaget
När kundbeskedet blev ett avslutsvillkor i klienten uppstod en fälla: knappen "Lämna åtgärdsförslag till kund" visades bara när ingen åtgärd var dokumenterad, och beskedsrutan bara när ett förslag fanns. En tekniker som skrev åtgärden först hade därmed ingen väg att registrera beskedet, och ärendet gick inte att stänga. Grinden avvisade redan sådana ärenden vid synk — villkoret i klienten flyttade bara upptäckten. Men ett spärrvillkor utan väg ut är ett fel även när spärren i sig är riktig. Förslagsknappen står kvar tills ett förslag faktiskt lämnats. Ordningen förslag → besked → arbete är fortfarande den panelen föreskriver, och loggen är append-only med tidsstämplar, så ett besked som registreras i efterhand syns som just det. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
8daeb41ba1 |
Kräv kundens besked även i klienten innan avslut
Grinden på servern har alltid krävt kundbeskedet när arbete faktiskt utförts. Klientens kanAvslutas gjorde inte det, och glappet syntes först när tio ärenden av olika karaktär kördes hela vägen genom gränssnittet: samtliga tio gick att stänga på skärmen med utfört arbete och utan att kunden hade sagt ja. Det är samma sorts fel som slutsatsen hade, på ett annat villkor — och det värre av de två, eftersom ett utfört arbete utan registrerat besked är precis vad en tvist handlar om. Hindertexten namnger nu kundbeskedet, och ett test speglar klientens avslutsvillkor mot grindens hela åtgärdskedja så att nästa villkor som läggs till i grinden inte kan glömmas i klienten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
6fa7953012 |
Logga metodikens mätkontroller som mätvärden
En kontroll vars krav är "matvarde" loggade bara kontroll_utford. Det gällde 56 av metodikernas 153 kontroller — drygt en tredjedel — och fick två följder som bägge upptäcktes när tio ärenden kördes igenom gränssnittet: Felorsaksanalysen nekade "Mätresultat" som underlag med motiveringen att underlaget inte finns i loggen, trots att teknikern just hade matat in mätvärdet som kontrollen krävde. Enda vägen förbi var att mata in samma siffra en gång till via dokumentationspanelen. Evidensprofilen i analysvyn underskattade systematiskt hur mycket som faktiskt mäts, eftersom mätningarna aldrig fanns som mätvärden. Mätningen loggas nu som det den är. Utan känt mätdon graderas den E1 och inte E4 (QUALITY-AUDIT M-1) — ett avläst tal utan spårbart instrument är inte en spårbar mätning. Tre tester låser förhållandet mellan metodikernas kravtyper och den evidens de producerar, så att en ny kravtyp inte kan införas utan att någon tar ställning till vad den ska ge för evidens. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
f0f5cc670e |
Håll ALVA-ytan fri från butikens driftmeddelanden
Betalningsbannern satt i App och undantog bara /felsokning, så den renderades ovanför ALVA-huvudet: "Skarpa betalningar är inte konfigurerade." En verkstad som ser ett driftmeddelande från en annan produkt läser det som att den tittar på något halvfärdigt. Det är ett trovärdighetsproblem, inte ett kosmetiskt. Undantaget jämför nu hela sökvägssegment i stället för prefix, så att /alvarlig inte råkar räknas som ALVA. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
590fab3a70 |
Spärra avslut utan slutsats även i klienten (ALVA-RULE-200)
Kvalitetsgrinden på servern krävde en slutsats, men klientens avslutsknapp gjorde inte det. Ett ärende gick därför att stänga på skärmen utan ett varför, och teknikern fick beskedet först vid synk — den sämsta tänkbara tidpunkten, eftersom bilen då har lämnat verkstaden. Klienten anropar nu granskaSlutsats() — samma funktion som grinden, inte en klientkopia av regeln — och avslutsknappen är spärrad tills den är ren. Slutsatspanelen visas så fort underlaget är helt, inte bara när metodikens sista steg är avbockat, så att frågan kommer när den går att besvara och inte som ett hinder efteråt. Hindret säger vad som fattas i stället för att bara konstatera att kraven inte är uppfyllda: först underlaget, sedan de enskilda bristerna i slutsatsen. Fyra tester jämför verdikt mellan klient och grind för samma logg, så att de två ändarna inte kan glida isär igen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
cd6b2caca5 |
Sammanfattning och slutsats i rapport och Live Share; API dokumenterat
Sammanfattningen ligger först i både kundrapporten och Live Share. Mottagaren är oftast inte tekniker — det är kunden, en försäkringshandläggare eller en flottansvarig — och de ska få bilden på fem sekunder och sedan kunna gå djupare, inte tvärtom. I Live Share härleds den ur det NIVÅFILTRERADE underlaget. Det betyder att sammanfattningen aldrig kan avslöja något som delningsnivån döljer: filtret ligger före projektionen, inte efter. Slutsatsen visas som ett eget avsnitt före underlaget. En handläggare läser skälet först och kontrollerar det sedan — den ordningen är hela poängen med att fältet finns. Sju nya vägar dokumenterade i OpenAPI och låsta av paritetstestet: sammanfattning, protokollinläsning, statistik, integrationskategorier, prenumerationer, radering och mätdon. Ett API som inte är dokumenterat är inte ett API någon kan koppla in sig mot — och specen valideras maskinellt så dokumentationen inte kan glida från servern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
3ec25dea9c |
ALVA: analysvy, sammanfattning och integrationsgränssnitt
---- Statistiken är ett produktbeslut ----------------------------------
Vilka siffror som visas avgör vad organisationen optimerar, så tre mått
är uteslutna med avsikt och låsta av test: ärenden per tekniker,
genomsnittlig ledtid och aktivitet. Alla tre belönar den som hoppar över
kontroller, och ett svårt fel SKA ta längre tid.
Medtagna, var och en handlingsbar:
VERIFIERINGSGRAD Andel avslut med fastställd orsak. Det enda mått
ett försäkringsbolag egentligen bryr sig om. Att
den inte är 100 % är friskt.
REPRODUKTIONSGRAD Låg siffra förutsäger återkommande fordon — man kan
inte åtgärda det man inte sett.
OMARBETNING Samma fordon tillbaka med samma orsakskategori inom
ett fönster. Det dyraste felet i en verkstad och det
enda ingen mäter, eftersom det kräver att två
ärenden kopplas ihop. Här är kopplingen gratis.
UNDANTAGSFREKVENS Vilka kontroller som hoppas över, per steg och fas.
Ett steg högt i listan är antingen felskrivet eller
kräver utrustning som saknas — bägge åtgärdbara.
Ett mått utan underlag visas som NOT APPLICABLE, aldrig som noll.
Skillnaden avgör om någon fattar beslut på en siffra som inte finns.
---- Sammanfattningen är härledd, inte genererad -----------------------
Frestelsen är att låta modellen skriva den. Det vore fel av tre skäl:
den blir en del av ett beslutsunderlag och måste därför gå att lita på
utan att granskas mot loggen varje gång; samma ärende måste ge samma
text om två år; och verkstadsgolvet har dålig täckning.
Den är alltså en projektion som briefen och rapporten. Den innehåller
inget som inte står i loggen och säger uttryckligen när något saknas i
stället för att utelämna det. Ett test kräver att den aldrig skriver
"felet konstaterat" när orsaken inte fastställts.
---- Integration utan påhittade endpoints ------------------------------
Jag känner inte Beonodes, ServiceCams eller CABAS faktiska gränssnitt.
En uppfunnen endpoint som ser färdig ut är sämre än en tom: den ser ut
att fungera tills någon försöker.
Därför ett profildrivet gränssnitt. En profil beskriver vad en KATEGORI
av system förväntar sig, och märks validated först efter att den körts
mot leverantören. Tills dess står den som draft, och det syns i
gränssnittet — en integrationslista där allt ser färdigt ut är den
snabbaste vägen till ett misslyckat införande, eftersom verkstaden
planerar efter den.
Kategorier: diagnosprotokoll, DMS, videooffert, fordonsdata,
skadekalkyl (CABAS är nordisk standard), garanti och försäkring.
Skadekalkyl går åt båda håll. Det ALVA tillför en kalkyl är inte fler
poster utan beviskedjan bakom dem: vad som kontrollerades, vad som
uteslöts och varför. Det är den enda del av en kalkyl som i dag inte går
att granska i efterhand.
Inkommande protokoll blir evidens, inte bilagor, med härkomsten bevarad
i varje post — ett värde som kommit utifrån får aldrig se ut som något
teknikern själv mätt. Saknas instrumentets identitet nedgraderas värdet
till E1 enligt samma regel som gäller manuella mätningar.
Utgående leveranser signeras med HMAC över tidsstämpel och kropp;
tidsstämpeln ligger inne i signaturen så en fångad leverans inte går att
spela upp i morgon. Verifieringsfunktionen exporteras så mottagaren kan
använda exakt samma kod — de flesta integrationsfel uppstår i glappet
mellan två implementationer av samma signatur.
Prenumerationer går genom samma SSRF-gräns som leverantörsuppslagen, och
leverans sker efter att loggen skrivits: en mottagare ska aldrig kunna
se en händelse som inte finns i loggen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
01189ae42a |
ALVA-RULE-200: ett ärende stängs aldrig utan ett varför
Kravet är enkelt att formulera och lätt att bygga fel. En obligatorisk
fritextruta blir "klart" på tredje ärendet, och då har vi bara gjort
dokumentationen långsammare utan att göra den bättre.
Därför fyra frågor med olika adressat i stället för ett fält:
MOTIVERING Varför följer slutsatsen av evidensen? Detta är raden som
saknas i varje verkstadsprotokoll. Underlaget säger vad
som mättes, slutsatsen vad som är fel — ingenting säger
varför det ena medför det andra, och det är precis det
steget en försäkringsbedömare granskar.
UTESLUTET Vad övervägdes och varför föll det bort?
ÅTGÄRDSVAL Varför denna åtgärd och inte en annan?
KVARSTÅENDE Vad är fortfarande osäkert? Får vara "inget" — men aktivt.
Kvalitetsgranskningen är riktad mot hur någon med bråttom faktiskt
skriver: en katalog över icke-svar (klart, åtgärdat, trasig, se ovan,
vet ej), minsta längd, och kravet att texten bär ett orsakssamband eller
refererar konkret evidens. Den som skriver "12,4 V vid stift 14"
hänvisar till en mätning utan att säga ordet — regeln får inte tvinga
fram ett språkbruk som inte är teknikerns.
Den regel som gör underlaget användbart för ett försäkringsbolag härleds
ur loggen: en hypotes som dokumenterats och inte blivit slutsatsen MÅSTE
bemötas. Utan den är en felsökning en gissning som råkade stämma.
Den ärliga vägen finns: orsaken kunde inte fastställas är ett giltigt
utfall, ofta mer användbart än en påhittad orsak — men varför den inte
kunde det är fortfarande ett varför.
Slutsatsen är kunddelbar. Den besvarar "varför kostade det här vad det
kostade" och är den enda rad en bedömare behöver. Att bygga funktionen
och sedan hålla den intern vore att bygga den förgäves.
I gränssnittet granskas fälten medan man skriver, inte efter Spara, och
obemötta hypoteser listas — teknikern ska aldrig behöva gissa vad som
fattas. Det är skillnaden mellan ett krav som respekteras och ett som
kringgås.
---
Typkontrollen avslöjade under arbetet en riktig bugg i mitt eget
grindarbete: schemat och grinden skrevs mot antagna fältnamn i stället
för mot domänmodellen. atgard_utford heter beskrivning och utford, inte
text. Följden var värre än ett typfel — grinden såg utförd åtgärd som
utebliven och krävde därför aldrig kundbesked eller kvalitetskontroll.
Det syntes inte, eftersom testfixturerna hade samma antagande.
Rättat i schema, grind och fixturer, och låst av två nya test: varje
fält i schemat måste finnas i domänmodellen, och varje händelse
demoärendet producerar måste passera serverns validering. Ett schema som
avvisar riktig trafik är värre än inget schema.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
baa5288add |
ALVA-modellen synlig i guiden
Fasindikatorn ovanför metodikpanelen: var i metoden arbetet befinner sig, vilken fas det aktuella steget hör till, och vad fasen uttryckligen inte gör. Fasen härleds ur steget via den delade klassificeringen, så indikatorn kan aldrig visa något annat än vad metodiken faktiskt gör. En indikator som sätts separat blir förr eller senare fel, och då är den värre än ingen — den påstår att arbetet är i verifiering när det fortfarande lokaliserar. Ärendets beteckning står i samma rad. Ett id ur loggen är en ogenomskinlig sträng; ALVA-CASE-00007 är det som sägs i telefon och skrivs i ett protokoll. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
a8fdc9ebcc |
ALVA: webbplats, portal och kunskapskällor
Webbplatsen och plattformen är ett system. Samma komponentbibliotek, samma rutnät, samma ord — besökaren lämnar aldrig marknadswebben när den loggar in, eftersom den inte gör det. När apparna kommer återanvänder de samma bibliotek. Det som saknas på startsidan är lika avsiktligt som det som finns: ingen hjältebild, ingen gradient, ingen animation, inga kundlogotyper, ingen uppmaning att boka demo. En sida som försöker övertyga läser som marknadsföring; en sida som redovisar läser som dokumentation. Den senare är vad en teknisk chef fattar beslut utifrån. Kunskapskällorna är byggda som Operational Knowledge Infrastructure: leverantörsoberoende gränssnitt där organisationen ansluter det den har licens till. Det gör att produkten aldrig behöver marknadsföras med "vi stödjer X". En betainstallation fungerar på enbart egna dokument. Livscykeln har fem tillstånd med avsikt. Configured och Connected är inte samma sak — en nyckel kan vara ifylld och ändå fel — och Validated skiljer sig från Connected: anslutningen svarar, men har den data för de fordon organisationen faktiskt arbetar med? Det är den frågan som avgör om källan är till nytta. Portalens nyckeltal visas som NOT APPLICABLE i stället för som tomma grafer. Att visa noll vore att lova något som inte finns; att dölja raderna vore att dölja riktningen. Ett test läser den faktiska källkoden och låser disciplinen: inga gradienter, ingen animation, inga emojier utanför ✓ ○ □ →, ingen färg utanför paletten, inga marknadsföringsord. Ett designsystem som bara finns i ett dokument blir urvattnat på tredje sidan någon lägger till. Testet avslöjade att min egen kod använde py-3 — 12 px — och alltså bröt 8 px-rutnätet på sju ställen. Jag rättade koden, inte testet. Ett rutnät med undantag är inget rutnät. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
610bb9eca6 |
ALVA: metoden i kod — faser, nomenklatur, statusspråk, komponenter
ALVA är inte en omdöpning utan en annan produktklass. Skillnaden mellan en assistent och en metod är att metoden går att granska, och därför ligger den i kod och i test, inte i marknadsföringen. Språkgränsen är den bärande avgränsningen: ALVA:s STRUKTUR är engelsk och oföränderlig — fasnamn, statusord, dokumenttyper, identifierare skrivs likadant i varje land, precis som DIN 2014 heter DIN 2014 på svenska. ALVA:s INNEHÅLL är på arbetsspråket: frågan till teknikern och kontrollpunktens text är svenska i en svensk verkstad. Att översätta VERIFICATION till Verifiering hade gjort ALVA till ett ord för samma sak i varje land i stället för samma sak i varje land. Faserna är tillämpade på metodikbiblioteket, inte påklistrade: alla 31 steg-id i de 16 metodikerna är klassificerade i data. Gränsen mellan L och V är den enda svåra och bär hela metoden — att mäta spänning vid en komponent avgränsar var felet finns, att mäta spänningsfall under last fastställer varför. Regeln: ett steg är Verification när det kan avfärda en kandidatorsak. Ett test kräver att faserna kommer i ALVA-ordning inom varje metodik. Ett annat slår fast att en metodik inte behöver innehålla alla fyra — läckagemetodiken slutar med lokalisering, och att fylla ut modellen med ett konstruerat Action-steg hade varit att tillämpa den slarvigt. Statusspråket är en uttömmande katalog, låst av test: inga utropstecken, inget tilltal, varje rad slutar med punkt. Bedömning uttrycks som ett tal — Confidence level: 92% — aldrig som Jag tror. Skillnaden är produktens existensberättigande: det ena är en person som gissar, det andra ett mätvärde som går att ifrågasätta. Komponentbiblioteket är ett, för både webbplats och portal. 8 px-rutnät utan undantag, nästan monokromt, ALVA Blue bara för aktivt steg och verifierad status. Fyra ikoner: ✓ ○ □ →. Ingen animation — rörelse som inte bär information är brus i ett utrymme där teknikern redan har för mycket. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
03fbea8fac |
Revisionen: m-4 och m-6 — de två som bara går att stänga delvis
m-6 · Tillgänglighetstester med axe på verkstadsgränssnittets grundkomponenter: knapp, textfält, panel och bild. Gränssnittet var genomtänkt för handskar, buller och solljus men obevisat — ingenting kontrollerade att en knapp har ett tillgängligt namn eller att ett formulärfält har en etikett. Att bilder bär alternativtext är i den här produkten inte bara ett tillgänglighetskrav: bilden *är* bevisningen, och beskrivningen är det som säger vad den visar. Ungefär en tredjedel av WCAG går att kontrollera maskinellt. Testet säger det rakt ut, och den manuella granskningen står kvar som öppen — automatik hittar inte om ett gränssnitt är användbart, bara om det är felformat. m-4 · Paritetstestet mellan orkesterkopiorna jämför nu innehåll i stället för att kontrollera att strängar förekommer: grundreglerna ord för ord, metodikkatalogens id i ordning, och modellvalet per uppgift. Ett substrängtest hade inte fångat att en grundregel ändrats i ena kopian. Den riktiga åtgärden är att avveckla Supabase-vägen. Det är ett driftbeslut, inte en kodändring, och står kvar som öppet. Revisionsdokumentet har fått en avdelning med utfall per fynd, så att en omgranskning kan verifiera i stället för att lita på ordet. Två punkter är ärligt öppna och ingen av dem är kod: personuppgiftsbiträdesavtalet med konsekvensbedömning, och avvecklingen av den äldre orkestern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
fbc034a280 |
Revisionen: C-3, C-4, M-1, M-4, M-5, M-6, m-3, m-5 och m-7 åtgärdade
C-3 · Dataskydd genom krypto-shredding. Identifierande fält krypteras med en nyckel per ärende; radering sker genom att nyckeln förstörs. Loggen förblir intakt och hashverifierbar — det som blir oåtkomligt är identifieringen, inte protokollet över vad som kontrollerades. Ett raderat ärende visar fortfarande att lufttrycket mättes till 2,4 bar klockan 08:42, bara inte längre vems bil det gällde. Vad som inte krypteras är lika viktigt: mätvärden, observationer och kontrollresultat är verksamhetsdata. Krypteras allt raderas beviset tillsammans med personuppgiften. En raderingsbegäran gäller ett fordon, inte ett ärende — men identifieraren är krypterad och går inte att söka på. Därför ett blindat index: HMAC av den normaliserade identifieraren, samma fordon ger alltid samma värde, värdet går inte att vända tillbaka utan nyckeln. Gallringsdatum sätts vid avslut utifrån ärendetypen. Ett ärende utan datum gallras aldrig — för tidig gallring går inte att ångra. C-4 · Modellanropen kan stängas av per organisation. Flaggan bärs i token så orkestern kan neka utan databasåtkomst. Metodikmotorn fungerar ensam; en verkstad som inte kan acceptera överföringen till modelleverantören kan ändå använda produkten. M-1 · Mätvärden bär vilket mätdon som användes och när det var kalibrerat. Utan spårbart instrument nedgraderas värdet från E4 till E1 — det är teknikerns observation av en siffra, inte en mätning. Kalibreringen bedöms vid mättillfället, inte i dag. Demoärendet fick kalibrerade instrument: det ska visa den praxis produkten kräver. M-4 · Läslogg. Varje skrivning loggades redan; ingen läsning gjorde det. M-5 · Återställningstest i CI. Larmet visade att backup sker, inte att den går att återställa. Testet kontrollerar det som faktiskt brukar tappas: att append-only-triggarna följde med och fortfarande biter. M-6 · Regelpaketet verifieras mot HMAC. Ogiltig signatur spärrar avslut; saknad nyckel ger granskningsläge i stället för driftavbrott — det är så säkerhetsfunktioner blir avstängda. m-3 · EXIF-borttagningen är nu avsiktlig och låst av test. Den höll av en slump, och en ändring i stil med "bevara originalkvaliteten" hade tyst börjat publicera var kundens bil stod. m-5 · SBOM och sårbarhetsskanning i CI. m-7 · Promptversionen binds till varje svar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
e73decd3f5 |
Revisionen: C-1, C-2, M-2, M-3, m-1 och m-2 åtgärdade
De fyra fynd som underminerade bevisvärdet sitter i samma kodväg och åtgärdas därför tillsammans. C-1 · Härkomsten sätts av servern. anvandare kommer ur den verifierade token och tidpunkt ur serverns klocka. Klientens tidsstämpel kastas inte — vid offline-arbete är den det enda som finns — utan bevaras som registrerad_tidpunkt bredvid mottagningstiden, så glappet blir synligt i stället för osynligt. C-2 · Kvalitetsgrinden flyttad till services/gemensam/grind.mjs och utvärderas nu på servern vid arende_avslutat. Ett avslut som inte passerar får 409 med de faktiska hindren. Metodikdatan flyttades till services/gemensam/metodiker.mjs och klientens metodiker.ts är en typad återexport, så det finns fortfarande exakt en sanning. M-2 · Högvoltsspärren är ett hinder. Ett nekande svar på behörighets- eller spänningsfrihetsfrågan stoppar metodiken i stället för att räknas som besvarad, och spärrar avslutet på servern. Ett obesvarat säkerhetskrav spärrar också — tystnad är inte ett ja. Reglerna ligger i data så nästa farliga metodik bara behöver en rad. M-3 · Händelser valideras mot schema före skrivning. Loggen är append-only, så en felaktig post kan aldrig rättas — kontrollen måste ske innan, efteråt är det för sent för alltid. Okänd typ avvisas. m-1 · Delningskoden använder förkastningsurval i stället för modulo. m-2 · Id-kollisioner räknas i stället för att passera tyst. 21 nya tester låser varje regel. Ett skräddarsytt test kräver dessutom att varje typ i händelseschemat är klassificerad i delningslistan — en ny typ kan alltså varken läcka eller tappas bort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
cdb3320c4a |
Kvalitetsrevision ur en OEM-kunds perspektiv
En genomgång av koden — inte av dokumentationens påståenden — med den granskning en fordonstillverkares leverantörskvalitet skulle göra. Dokumentet säger uttryckligen att det är en intern övning med Volkswagen AG som referenskund, inte en revision utförd av eller på uppdrag av dem. Fyra kritiska fynd, varav två underminerar produktens kärnlöfte: Servern tar emot anvandare och tidpunkt från klienten och kontrollerar bara att de är strängar respektive sanningsvärda. JWT:n bär redan den verifierade identiteten men används inte på den vägen. En append-only logg vars författare och tidpunkt sätts av anroparen är inte en revisionskedja. Kvalitetsgrinden finns bara i webbläsaren. Ett ärende kan avslutas utan evidens genom ett anrop till API:t, och rapporten presenterar det som komplett. Dokumentationen säger att systemet aldrig kan skriva en slutsats ECM inte godkänt; det stämmer inte vid API-gränsen. De två övriga är rättsliga: append-only utan raderingsväg går inte att förena med dataskyddsförordningens artikel 17, och överföringen av kunduppgifter och VIN till modell-API:t saknar dokumenterad grund. Sex allvarliga fynd, bland dem att mätvärden rankas som hög evidens utan att bära vilket instrument som mätte eller om det var kalibrerat, och att högvoltsteget går att svara nej på och ändå fortsätta. Dokumentet listar också det som håller — och varför det bör skyddas i åtgärdsarbetet. En revision som bara räknar fel ger en falsk bild. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
031d6d5a3d |
All produktdokumentation på engelska som källa
De återstående dokumenten — VISION, MVP, DEMO, DRIFT och MASTER-PROMPT — får engelska versioner, och de svenska blir översättningar med samma notis som systembeskrivningen redan hade. DRIFT.md heter nu OPERATIONS.md; ett svenskt filnamn på ett engelskt dokument hade varit inkonsekvent. Dokumentationen är därmed 33 filer: sexton dokument med engelska som källa, sexton svenska syskon, och systembeskrivningen dessutom på tyska, danska och norska. Två fel hittades under översättningen, båda rättade i bägge språken: DRIFT.md pekade ut .github/workflows/ci.yml som Guidad Felsöknings CI och sa tre rader längre ned att samma fil tillhör Semantika och inte rörs. Det stämde innan CI flyttades till egen Gitea; rätt fil är .gitea/workflows/felsokning.yml. MVP.md kallade styrdokumentet Master Prompt v1.0 men länkade till v2.0. Att översätta ett dokument är den grundligaste läsning det får. Båda felen hade överlevt flera genomgångar av samma text på svenska. Kodidentifierare, miljövariabler, sökvägar och UI-etiketter står oöversatta i de engelska versionerna. Att skriva "Create demo case" i demomanuset hade gjort manuset obrukbart — knappen heter "Skapa demoärende". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
86efbaaca5 |
Moduldokumenten på engelska som källa
De åtta moduldokumenten och exempelflödet får engelska versioner. Kataloger och filnamn följer med: moduler/ → modules/, exempel/ → examples/, och de svenska filnamnen ersätts av engelska. Ett engelskt dokument i moduler/arendebrief.md hade varit inkonsekvent. Bytet gjordes med git mv så historiken följer med, och interna länkar i de svenska versionerna pekar nu på svenska syskon i stället för på filnamn som inte längre finns. Kodidentifierare och JSON-exempel står oöversatta även i de engelska versionerna — falt, hemlig, uppslag och svarsfalt är fältnamn i integrationer.json, inte prosa. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
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 |
||
|
|
3c5752898b |
Systembeskrivningen på engelska, tyska, danska och norska
Fyra översättningar av hela dokumentet — inte sammanfattningar. Alla 48 numrerade avsnitt finns i varje språkversion, verifierat maskinellt. Kodidentifierare är inte översatta. Händelsetyper, funktionsnamn, fältnamn, filsökvägar och konfigurationsnycklar är svenska i själva koden; ett dokument som döper om dem till engelska blir oanvändbart mot repot. De står därför ordagrant, med förklaring på målspråket där betydelsen inte är uppenbar. Ett skript kontrollerar att 38 sådana identifierare överlevde översättningen i alla fem filer. Varje översättning säger i ingressen att den svenska versionen är källan och gäller vid avvikelse. Två dokument som påstår sig vara lika auktoritativa blir i praktiken två sanningar. Avsnittet om nyckelordsstammar har fått ett tillägg per språk: att "partikelfilter" inte matchar "partikelfiltret" är en egenskap hos svensk böjning, och tyska sammansättningar respektive dansk och norsk bestämd form ger samma klass av problem. Ett lokaliserat nyckelordsset måste valideras mot samma test, inte översättas ord för ord. Ordlistan går från svenska till målspråket och är därmed mer användbar i översättning än i originalet — den blir nyckeln mellan koden och läsaren. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
e90cbf99e9 |
Fullständig systembeskrivning i ett dokument
Ett självständigt referensdokument som beskriver hela systemet utan att repot behöver vara tillgängligt: principer, domänmodell, metodikmotor, ECM, tjänster, säkerhet, infrastruktur, testning och repostruktur. Innehållet är hämtat ur koden, inte ur planer. Där något inte finns står det uttryckligen — inklusive ett avsnitt om kända begränsningar och öppna punkter, eftersom ett dokument som bara beskriver det som fungerar är svårare att resonera kring än ett som säger var kanterna går. Ett eget avsnitt samlar designbesluten med sina motiv. Motivet är oftare det som behövs vid en diskussion än beslutet i sig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
1bb40319d8 |
Metodikbiblioteket: 16 metodiker som täcker fordonets system
Tre metodiker räckte för att visa principen men inte för att arbeta.
Biblioteket flyttas till en egen fil och växer till sexton: vibration,
bromsar, styrning/fjädring, elsystem, start & laddning, motorgång,
kylsystem, drivlina, avgas & emission, klimat, högvolt, felkoder &
kommunikation, läckage, missljud, ADAS — plus generisk.
Att "täcka allt" går inte att lova. Det som går är att täcka systemen
systematiskt och låta generisk vara ett strukturellt komplett skyddsnät
för det ingen förutsett.
Tre regler gäller alla metodiker, och alla tre är låsta av test:
1. Varje kontroll har ett minimikrav — mätvärde, foto eller
observation. En kryssruta är inte evidens.
2. Varje metodik börjar med att verifiera symptomet, aldrig med att
åtgärda. Kundens ord blir ett symptom först när det reproducerats.
3. Där arbetet kan skada någon ligger säkerhetssteget först.
Högvoltsmetodiken kan inte påbörjas utan dokumenterad
spänningsfrihet, urtagen servicebrytare och skyddsutrustning —
det arbetet kan döda, och en kryssruta duger inte.
Motorn och innehållet skiljs åt: metodik.ts äger typer, val och
härledningen av nästa steg, metodiker.ts äger metodikerna. Biblioteket
kan växa utan att motorn ändras.
Valet av metodik är inte längre en regexkedja utan poängsatt på
nyckelord, där det längre — mer specifika — ordet väger tyngre:
"traktionsbatteri" slår "batteri". Korta ord matchas som helt ord,
längre som ordstam, annars hade "ac" träffat "acceleration" och en
vibration hamnat i klimatanläggningen. Nyckelorden är stammar, inte
färdigböjda ord: svensk böjning kapar ofta ett e (filter → filtret), så
"partikelfilter" hade aldrig matchat texten teknikern faktiskt skriver.
Valet är en frågeordning, inte en diagnos. Träffar inget blir det
generisk — ett ärligt "vi vet inte var vi ska börja" i stället för en
gissning — varefter orkesterns klassificerare får försöka.
Orkesterns metodikkatalog byggs nu ur en lista i stället för att räknas
upp i både schema och prompt, i båda kopiorna (ai-orkester och
edge-funktionen). Ett test jämför den mot klientens: glider listorna
isär returnerar klassificeraren ett id klienten inte känner igen, och
valet faller tyst tillbaka på generisk.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
69a519be75 |
Observation: se var tiden går, utan ett enda nytt beroende
CloudWatch gav loggar och mätvärden men svarade inte på frågan man faktiskt har när något är långsamt: var tog tiden vägen. Tjänsterna har medvetet nästan inga beroenden — plattformen har pg-drivrutinen, orkestern har Claude-klienten. 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 Context. Klienten startar spåret och traceparent följer med genom plattformen till orkestern, så en teknikers handling går att följa hela vägen till modellsvaret i stället för att bli två orelaterade spår. CloudWatch EMF. Strukturerad JSON som CloudWatch själv extraherar mätvärden ur — ingen agent, ingen SDK, inget som kan sluta fungera tyst. Varje anrop ger en loggrad med nedbrytning av tiden per del: databasen, modellanropet, objektlagringen, kundens leverantör. Det svarar direkt på om ett långsamt ärende beror på S3 eller på Opus-granskningen, i stället för att någon ska korrelera fem loggrader. Vägen normaliseras innan den blir dimension, och organisation, ärende-id och spår-id blir aldrig dimensioner — varje unik kombination är en egen tidsserie som kostar. De ligger som vanliga fält, sökbara i Logs Insights. Ett test låser det, eftersom det är precis den sortens sak som smyger in senare. Tre larm på det teknikern märker: svarstid p95 över tre sekunder (medelvärdet döljer att var tjugonde tekniker väntar orimligt länge), serverfel med spår-id i loggraden, och att modellen avböjer — det senare tyder på att underlaget innehåller något oväntat, inte på ett driftfel. Modulen är delad mellan tjänsterna i stället för duplicerad. Byggkontexten flyttas därför till felsokning/services, och en symlänk gör att testerna och integrationstestet kör mot samma fil som bilderna. Verifierat: 106 vitest-tester (10 nya för spårning, EMF-format och att dimensionerna hålls få), typkontroll, eslint på klient och tjänster, rotens CI, terraform fmt och referenskontroll på båda lagren, samt integrationstest mot riktig Postgres där spårraderna syns live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
e94a97715a |
Kartan och driftshandboken stämmer med AWS igen
Infrastrukturen byttes ut men dokumentationen beskrev fortfarande den gamla världen: kustomize, Argo CD, nginx-ingress, Postgres i klustret, GHCR och tre databaslägen. En inaktuell karta är sämre än ingen — den lurar den som litar på den. Infrakartan är omritad mot AWS: tjänsterna och var de ligger, den egna bygg- och driftkedjan från commit till kluster, gränserna inklusive IRSA-bindningen, nätets tre lager och de fyra sakerna som återstår innan skarp drift. Ärendets flöde är oförändrat — produkten är densamma, det är grunden under den som bytts. DRIFT.md beskriver nu de två Terraform-lagren och varför de är två, Aurora med PITR i ett subnätlager utan routing ut, hemligheterna i Secrets Manager där Terraform aldrig ser värdena, och CD-kedjan över Gitea med egna runners. MVP.md har fått en rad för AWS-driften. Kartan säger också rakt ut vad som INTE verifierats: terraform validate kunde inte köras eftersom registry.terraform.io var blockerad, så det som kontrollerats är formatering och statisk referenskontroll. Det står först i listan över vad som ska göras innan skarp drift. Verifierat: 96 vitest-tester, rotens lint och format:check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
a5e276534c |
Arbetslasten kopplad till AWS — kedjan är helt egen
Andra lagret läser nu basens utdata i stället för att upprepa någonting. 05-aws.tf hämtar klustret, domänen, certifikatet, registret, rollerna, hinken och hemligheternas namn — ändras något i basen slår det igenom här utan att en rad ändras. Autentiseringen mot klustret sker med en färsk EKS-token i stället för en kubeconfig på disk: inget att distribuera, inget som går ut. Hemligheterna bor i Secrets Manager och speglas in av External Secrets var timme. Terraform ser aldrig värdena, och det är själva poängen — en hemlighet som passerar Terraform hamnar i tillståndsfilen. Roteras en hemlighet följer klustret efter av sig självt. Plattformstjänsten kör som ett tjänstekonto med IRSA. Rollen är bunden till exakt det kontot i den namnrymden, så grannpodden på samma nod får ingenting på köpet. Bilagorna signeras mot S3 med samma roll — inga nycklar existerar att läcka. Postgres-podden är borta. Händelseloggen ligger i Aurora utanför klustret, i ett subnätlager utan routing ut, med automatisk säkerhetskopiering och PITR. Ingressen är en ALB med ACM-certifikat i stället för nginx, och alla tre tjänsterna delar den genom samma gruppnamn. Gitea med egna Actions-runners kör i samma kluster. Därmed finns inget externt beroende kvar: källkod, bygge, register och driftsättning ligger allt i vår AWS-miljö. Runnern och driftsättningen har varsitt tjänstekonto med skilda roller — bygget får publicera till ECR men inte röra klustret, driften tvärtom. Ett komprometterat bygge kan inte driftsätta. Gitea kör SQLite på en EBS-volym i stället för en egen Aurora-databas. En verkstadsinstallation har en handfull utvecklare, och ytterligare en databas att säkerhetskopiera och övervaka är kostnad utan motsvarande nytta. Verifierat: 96 vitest-tester, rotens lint och format:check, terraform fmt på båda lagren och statisk referenskontroll av vardera (88 respektive 35 resurser, inga dinglande referenser, alla count-resurser indexerade). terraform validate kunde inte köras här — registry.terraform.io är blockerad av sessionens egress-policy — så CI-jobbet kör den. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
ac24938108 |
AWS-basen: allt eget, inget GitHub
Guidad Felsökning får en egen AWS-grund i felsokning/infra/aws — nät, kluster, databas, objektlagring, register, hemligheter, domän och observation. Inget GHCR, ingen extern byggtjänst. Två lager i ordning, och uppdelningen är inte smak: en enda apply som både skapar ett EKS-kluster och schemalägger in i det är en känd fälla, eftersom kubernetes-leverantören måste konfigureras med uppgifter som inte finns förrän klustret existerar. Nätet ligger i tre lager. Publikt bara för lastbalanserare och NAT, privat för noderna, och ett eget datalager för Aurora utan routing ut alls — att databasen inte kan nå internet hänger då inte på att en säkerhetsgrupp är rätt konfigurerad. VPC-endpoints för S3, ECR, loggar, Secrets Manager, STS och ELB gör att bilddragningar och loggar aldrig lämnar nätet, vilket både är säkrare och sänker NAT-notan rejält. IRSA i stället för nodroller: plattformens tjänstekonto har en roll bunden till exakt en namnrymd och ett tjänstekonto, så grannpodden på samma nod får ingenting på köpet. IMDSv2 med hoppgräns 1 hindrar dessutom en pod från att låna nodens roll via metadatatjänsten. Bygge och drift har delade rättigheter. Byggrollen får publicera till ECR men inte röra klustret; driftrollen tvärtom. Ett komprometterat bygge kan därför inte driftsätta. Aurora PostgreSQL Serverless v2 med 30 dagars säkerhetskopiering och PITR ned till sekunden — det som gör den duglig för händelseloggen är inte prestandan utan att förlorad logg annars hade tagit varje ärendes bevisvärde med sig. S3-hinken för bilagor har versionshantering, SSE-KMS, TLS-krav och blockerad publik åtkomst; plattformens roll får läsa och skriva men aldrig radera, eftersom bilagorna hör till en append-only logg. Larmen är fyra och ett av dem larmar på saknad data: uteblir mätvärdet för säkerhetskopiering finns ingen backup, och en backup man tror finns är värre än ingen. Verifierat: terraform fmt och en statisk referenskontroll (88 resurser, 5 datakällor, inga dinglande referenser). terraform validate kunde inte köras här — registry.terraform.io är blockerad av sessionens egress-policy — så CI-jobbet kör den i stället. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
c407aad58a |
Varje produkt äger sitt eget träd — och GitHub-flödena bort
Rot-CI:n föll med 107 lint-fel efter merge:n. Orsaken var inte kod som blivit sämre: Semantikas rot-eslint började granska felsokning/, ett träd med egen verktygskedja och andra regler. Felen låg i värdappens befintliga Supabase-funktioner, oförändrade sedan innan. Rätt åtgärd är att låta varje produkt äga sitt eget träd, inte att lätta på någons regler. felsokning/ ignoreras därför av Semantikas eslint och prettier — den har egen eslint-konfiguration, egna prettier-regler och eget CI-flöde. Ingen regel är försvagad för någon av produkterna. Samtidigt: mina GitHub-workflows för publicering och driftsättning är borttagna. Guidad Felsökning ska inte längre bero på GitHub Actions eller GHCR — bygge, publicering till eget ECR och terraform apply mot EKS ligger nu i .gitea/workflows/felsokning.yml och körs av egna runners. Driftsättningen är fortsatt ett eget, manuellt steg med en bildtagg; rollback är att köra igen med en tidigare tagg. .github/workflows/ci.yml är Semantikas och lämnas orörd. Verifierat: rotens lint, format:check, typecheck och test går igenom, och Guidad Felsöknings egna 96 tester, typkontroll, bygge och integrationstest mot riktig Postgres likaså. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
301c477e25 |
Merge main: Guidad Felsökning flyttar ur roten till felsokning/
Main är sedan den här grenen skapades en helt annan produkt — Semantika,
en mobilapp med egen CDK-infrastruktur. Den äger nu repots rot: en
npm-workspaces-monorepo med apps/mobile, services/api och infra.
För att båda ska rymmas i samma repo flyttar Guidad Felsökning in i en
egen katalog i stället för att göra anspråk på roten:
felsokning/app webbklienten (Vite, egen package.json och
eslint-/vitest-konfiguration)
felsokning/services plattformstjänsten och AI-orkestern
felsokning/infra Terraform och databasschemat
felsokning/docs vision, moduler, drift
felsokning/supabase edge-funktion och migrationer
Merge:n hade tagit bort 128 filer som Guidad Felsökning bygger på —
värdapplikationens komponenter, Supabase-klienten, tillgångar — eftersom
main raderat dem och den här grenen inte råkat ändra just dem. De är
återställda på sin nya plats. Utan dem gick varken bygget eller
testerna: ai.ts och synk.ts importerar Supabase-klienten.
Semantikas rotfiler är orörda: package.json, eslint.config.js och
.github/workflows/ är deras. Guidad Felsökning har egna motsvarigheter i
sin katalog.
CI flyttar samtidigt från GitHub Actions till .gitea/workflows — samma
syntax, egna runners. .github/workflows/ tillhör Semantika härefter.
Verifierat på den nya platsen: 96 vitest-tester, typkontroll, eslint på
både klient och tjänster, bygge, och integrationstest mot riktig Postgres.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
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 |
||
|
|
c20633be51 |
Semantika V1 — minimalist AI reflection partner (MVP) (#4)
Chat-first onboarding, model-driven paywall, Apple/Google subscriptions, AWS CDK infrastructure, curated knowledge base, store compliance docs. |
||
|
|
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 |
||
|
|
e5b81f46b0 |
Store readiness: in-app account deletion, privacy policy, terms, listing copy
Closes the remaining store-compliance gaps before App Store / Play submission: - Account deletion (App Store guideline 5.1.1): DELETE /me removes the user row (usage cascades). The app exposes it through one quiet 'Account' caption link under the chat, visible only when signed in, driving two native dialogs (Sign out / Delete account with a destructive confirm) — no new views, no menus, minimalism intact. Deletion signs out and resets the app; copy notes that store subscriptions are cancelled in App Store / Play settings. - docs/store/privacy-policy.md: the complete data inventory (matching the actual schema), transient OpenAI processing with no training, no profiling or ads, GDPR legal bases and rights, in-app erasure. - docs/store/terms-of-service.md: not-therapy positioning with crisis guidance, AI-generated-content caveat, 18+ eligibility, auto-renewal/cancellation terms, liability, Swedish governing law. - docs/store/listing.md: App Store and Play copy written to the honest-claims rule (subtitle 'Think Beyond Thought', keywords, descriptions), plus App Privacy and Data safety questionnaire mappings. - LAUNCH.md updated: host the policy/terms, set store URLs, use the prepared listing copy. - Tests: 30 passing (adds DELETE /me coverage). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
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
|
||
|
|
bf5c4e5f00 |
Beta readiness: full API test suite, EAS build config, launch checklist
Takes V1 from code-complete toward the closed beta: - Tests: the suite grows from 5 to 29. handler.test.ts covers route dispatch, guest device-id validation and user creation, JWT provider derivation, the paywall flag pass-through, free/premium mode selection, the assistant-final transcript rule (post-unlock delivery), the 402 abuse cap and its premium exemption, and purchase-verify validation. chat.test.ts covers structured-output parsing, premium masking of analysis_ready, the exact Responses API payload (knowledge base + strict JSON schema + the no-manufactured-suspense rule) and error handling. subscription.test.ts covers the Apple adapter (active/expired/wrong-product/sandbox retry on 21007) and the Google adapter (real RS256 JWT signing against a generated key, token exchange, expiry, 410-gone), with fetch and secrets mocked. - EAS: apps/mobile/eas.json with development/preview/production profiles for TestFlight and Play internal-testing builds. - LAUNCH.md: step-by-step path to 20-50 beta users — AWS deploy and secrets, migration, sign-in providers, store products at $5.99/$49.99, EAS builds, CI role, and the beta measurement plan (retention, dialogue depth, upgrade timing, tester interviews). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
720a8fdca4 |
Insight-first monetization: trust-building free tier and pricing
Adjusts the paywall strategy from withholding toward insight-first: - Free-tier instructions rewritten around the monetization philosophy: the goal is not to interrupt the conversation but to build trust. The free experience should leave the user feeling understood, respected and curious to continue, and should help the user reach a meaningful insight of their own. The rule is included verbatim: 'Never manufacture suspense. Create genuine curiosity by helping the user reach a meaningful insight, then offer a deeper level of analysis in Premium.' - Premium transition sharpened: analysis_ready may only fire when the conversation has reached a meaningful point and the natural next step is a complete analysis, structured framework, personalised strategy, practical exercises or a step-by-step action plan — pausing immediately before that delivery, never mid-sentence or mid- explanation. Transition example updated to the new wording. Product feeling encoded: always 'I genuinely want to continue this conversation', never 'they stopped me just to make me pay'. - Pricing: paywall now shows plan prices — localized prices fetched from the store via react-native-iap, falling back to $5.99/month and $49.99/year (~30% below monthly) until store config exists — plus the Premium includes list (unlimited conversations, unlimited analyses, personalised guidance, future feature updates). - README: monetization philosophy, product feeling and pricing sections updated to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
77d0322c09 |
Align codebase with the V1 production brief
Reconciles the implementation against the final MVP brief:
- Data model: rename users.provider to auth_provider and
users.subscription_status to subscription, matching the brief's schema
exactly (id, email, auth_provider, subscription, created_at). External
API field names are unchanged.
- Suggestions: replace the starter list with the brief's eight examples.
- Instructions: personality updated to calm, warm, intelligent, curious,
respectful, pedagogical — never judging, manipulative, dramatic,
overly positive, overconfident or preaching. Added per-reply goals:
feel personal, be calm, instill safety, give hope without promising
results, deepen thinking, and always contain at least one genuinely
new thought or question. Added the conversation outcome goal (greater
clarity, greater calm, a new perspective, increased trust in one's own
ability) and 'not a chatbot for general questions' to positioning.
- Paywall transition example updated to the brief's wording ('I'm
starting to see some recurring patterns… Unlock Premium to continue.').
- Knowledge base completed per the brief: communication models
(perceptual positions, observation vs interpretation, chunking,
backtracking, boundaries), reflection exercises (meaning audit,
meta-question, five frames, observer replay, well-formed outcome,
evening question) and a question library organized by purpose.
Appreciation in the Lift step must be anchored in what the user
actually expressed; the Challenge step never preaches.
- README: product principle (one user, one conversation, one analysis,
one recommendation), the removal rule, design words per the brief,
Definition of Done (7 steps), V2 not-now list (journal, saved
insights, community, coaches, courses, voice), and a closed-beta plan
for 20-50 testers with what the minimal data model can already
measure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z
|
||
|
|
8f7eca7fa8 |
Add Human Experience Doctrine and four-step conversation philosophy
Refines the product philosophy in the system instructions, the knowledge base and the README: - Meet users where they are: never assume the user is struggling — some seek change or guidance, others are simply curious and want to grow. - Human Experience Doctrine: every user should feel seen, respected, understood, capable and hopeful. The system must never create dependency or imply it alone has the answers. - Conversation philosophy: every conversation follows a four-step arc — acknowledge (reflect meaning, not words), explore (discover perspectives together, no interrogating or over-analyzing), lift (concrete, credible praise of resources and strengths — no generic compliments), challenge (leave at least one new perspective). - Personality: experienced coach, calm mentor, skilled teacher, wise conversation partner — never therapist, salesperson, preacher or guru. A thoughtful mentor rather than a stage performance. - Educational philosophy: teach the user how to reflect so they gradually need the tool less. - Roadmap in README: V1 is person-to-Semantika only; reflection groups (V2) and coaches/live sessions/courses (V3) come after the core product is strong. Community is deliberately absent from V1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
07234cc9a8 |
Rebrand as Semantika with honest, inspiration-based claims
Applies the brand platform consistently across the codebase: - Name: Semantika everywhere — app name/slug/scheme, bundle ids (com.semantika.app), workspace package names, CDK stack, Cognito domain prefix, secret name (semantika/app), database name, product ids (semantika_monthly/_yearly), and storage keys. - System instructions: Semantika is positioned as an intelligent reflection partner — not therapy, not self-help, not a course. It starts by exploring how the user interprets their situation and supports reflection and perspective-taking rather than delivering finished answers. Tone rules added: never judging, dramatic, overenthusiastic or preaching; always calm, curious, clear, respectful, structured, thoughtful. - Honest claims: instructions and knowledge base now explicitly frame neurosemantics and NLP as models and inspiration for reflection, not scientifically established methods — including a note that several NLP claims lack support in controlled studies. Claiming or implying proven effects is forbidden. - README: brand section (position, promise, mission, vision, positioning, tone, design words) and updated product philosophy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
6b7f0263e4 |
Add chat-first onboarding and intelligent context-based paywall
Replaces the hardcoded message limit with a model-driven paywall and removes registration before the first question: - Onboarding: the app opens directly into the chat. Dynamic conversation starters are served by GET /suggestions (services/api/suggestions.json), updatable with a deploy — no app release needed. Guests chat via POST /guest/chat, identified by an app-generated device id; sign-in moves to the paywall, where a purchase must attach to an account. - Free experience: the model runs in discovery mode — follow-up questions, pattern identification, visible understanding — building an analysis without delivering the full solution. - Intelligent paywall: the model returns structured output (reply + analysis_ready). Only when the problem is described, the information is sufficient and an action plan is ready does it write a calm transition and pause the conversation. Manufactured urgency, emotional pressure, fake readiness and mid-answer stops are explicitly forbidden. - Premium: on unlock the app resends the transcript and the backend immediately delivers the full analysis, strategies and exercises, then the dialogue continues without restriction. - The old FREE_MESSAGE_LIMIT becomes MESSAGE_CAP (default 200/30 days), kept purely as an abuse backstop — it is not the paywall. - WelcomeScreen removed; the app is now two views (Chat, Paywall). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
07fbeea09b |
Bygg NeuroSemantics AI: minimal mobilapp, en backend, IaC och CI/CD
Ersätter den tidigare webappen på denna branch med ett fokuserat monorepo: - apps/mobile: Expo/React Native med tre vyer (Welcome, Chat, Paywall), Cognito hosted UI-inloggning (Apple/Google/e-post) och In-App Purchase/Play Billing via en gemensam purchases-modul. - services/api: en enda Lambda-backend — OpenAI Responses API med Markdown-kunskapsbas som systeminstruktioner, free tier-gräns i PostgreSQL (HTTP 402 -> paywall) och kvittoverifiering bakom ett delat PaymentProvider-interface (Apple/Google, Stripe kan läggas till för webb senare). - infra: AWS CDK-stack med API Gateway (JWT-authorizer), Lambda, Cognito, Aurora Serverless v2 och Secrets Manager. - db/migrations: minimal datamodell (users + usage), inga konversationer sparas. - GitHub Actions: CI (lint, typecheck, test) och deploy från main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z |
||
|
|
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 |
||
|
|
8f1aba1571 |
Förhandsvisningen fungerar oavsett sökväg
Historik-routing kräver att sidan serveras från roten. Ligger den under en sökväg — som en publicerad enfilsförhandsvisning gör — matchar ingen route och besökaren får 404-sidan i stället för appen. Förhandsvisningsläget (VITE_HASH_ROUTER) går därför via hash, och dess startsida är Guidad Felsökning i stället för värdapplikationens startsida. Verifierat genom att ladda bygget från en djupt nästlad sökväg: startskärmen, navigering vidare och djuplänk till inställningar. Byggkommandot är nu dokumenterat i MVP.md tillsammans med npm run typkontroll, så förhandsvisningen inte byggs fel igen. 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 |
||
|
|
e22e0262e4 |
QR-/streckkodsläsning med typskyltsfallback
Sista posten i "objektidentifiering först" — och den sista avgränsningen från MVP:ns första version. - Kameraströmmen läser QR, Code 39/128, Data Matrix och PDF417 via webbläsarens BarcodeDetector; träffen fyller identifieraren och sätter rätt identifieringsmetod automatiskt - Avläst kod klassificeras alltid innan den används: VIN (17 tecken utan I/O/Q), svenskt regnr (båda serierna) eller serienummer. Identifieraren plockas ut även ur QR-innehåll som URL:er och vin=/regnr=-fält, medan fritext och nakna URL:er avvisas - Saknar webbläsaren API:t (iOS/Safari) fotograferas typskylten i stället och plattformens bildtolkning läser av den — kameran är gränssnittet oavsett enhet - Kameraströmmen stoppas alltid: vid träff, avbryt och avmontering. play() väntas medvetet inte in — på enheter där löftet resolvar först vid första bildrutan skulle avläsningen annars aldrig starta (fångat av klicktestet) 73 vitest-tester (nya: klassificeringen i alla varianter) och ett klicktest som kör båda vägarna — med och utan streckkods-API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
5428684cca |
Kundgodkännande före arbete — förslag, besked och spärrat arbete
Verkstaden kan inte längre utföra föreslaget arbete utan spårbart besked från kunden. - Ny händelse atgardsforslag: föreslagen åtgärd (förifylld ur felorsaksanalysen) med uppskattad kostnad — kunddelbar och visad i Live Share, där kunden ser förslaget och sitt registrerade besked - Ny händelse kundbeslut: godkänt/avböjt/delvis med obligatorisk kanal (telefon, på plats, e-post, SMS, delningslänk) och motivering vid avböjt/delvis; loggposten bär vem i verkstaden som tog emot beskedet - "Dokumentera utförd åtgärd" är låst medan ett förslag saknar besked och förblir låst vid avböjt — vägen "Ingen åtgärd utförd" är öppen och hänvisar till beskedet - Kvalitetsgrinden: besked obligatoriskt när arbete utförts, plus hård flagga "Utfört arbete trots avböjt åtgärdsförslag" - Rapporten får avsnittet "Åtgärdsförslag och kundens besked"; demoärendet visar hela kedjan förslag → godkännande → åtgärd Avgränsning: kunden lämnar sitt besked via kontakt med verkstaden som registrerar det. Publikt godkännande direkt i delningslänken kräver en skrivande publik endpoint och hanteras separat. 69 vitest-tester, integrationstest, OpenAPI-validering och klicktest (blockerat arbete utan/vid avböjt besked) gröna. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
636df0559d |
Åtgärdsfas med kvalitetskontroll — loopen sluts
Ärendet slutade tidigare vid rekommenderad åtgärd. Nu dokumenteras hela kedjan: symptomverifiering → felorsak → åtgärd → kvalitetskontroll. - Ny händelse atgard_utford: vad som gjordes (med delar) — eller varför ingen åtgärd utfördes, med orsak ur regelpaketet (kunden avböjde, väntar på reservdel, endast utredning beställd …) - Ny händelse kvalitetskontroll: symptomet borta / kvarstår / delvis / kunde inte verifieras, med beskrivning av hur verifieringen gjordes — under samma förhållanden som symptomet en gång reproducerades - Kvalitetsgrinden: "Åtgärd dokumenterad eller motiverad" obligatorisk vid avslut, "Kvalitetskontroll genomförd" obligatorisk när en åtgärd faktiskt utförts. Kvarstående symptom flaggas — ärendet bör inte avslutas som åtgärdat - Avslutsknappen spärrad tills kedjan är komplett; rapporten har eget avsnitt "Utförd åtgärd och verifiering" i beviskedjan - Demoärendet bär hela kedjan (ombalansering → verifierad provkörning) 65 vitest-tester, integrationstest och OpenAPI-validering gröna; klicktestet bekräftar att avslut öppnas först när kedjan är hel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
786e2159f5 |
Videoevidens (E3) och signerat avslut
Video med ljud är nu en riktig evidenstyp — den sista luckan i
evidensskalan E0–E6:
- Ny händelse video (beskrivning + dataUrl): Video-knappen i
Dokumentera-panelen spelar in/väljer ett kort klipp via kameran,
beskrivningen är obligatorisk ("det som låter eller rör sig") och
hård storleksgräns avvisar för långa klipp med tydligt besked
- Evidence Engine: video katalogiseras som E3 och räknas som oberoende
källa mot E6; underlagskällan "Video" i felorsaksanalysen valideras
nu mot loggen precis som foto/mätresultat
- Originalfilen bevaras och visas i arbetsloggen, kundrapporten och
Live Share-vyn
Signerat avslut (ur Completion-checklistan): "Avsluta felsökning"
loggar teknikerns signatur — "Felsökning avslutad — signerad av …" —
och kvalitetsgrinden redovisar signaturen.
61 vitest-tester, integrationstest, OpenAPI-validering och
preview-klicktest (beskrivningskrav, storleksgräns, videospelare i
loggen, signerat avslut) gröna.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|