Files
boc/workcamp/VOL6-Lessons-Learned.md
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

489 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AMOS Development Workcamp 2026
# Volume 6: Lessons Learned and Continuous Improvement
# Period: 2026-06-04 2026-06-07 (intensiv fas)
# Genererat: 2026-06-07 | Klassifikation: INTERN KONFIDENTIELL
---
## INLEDNING
Denna volym dokumenterar incidenter, rotorsaksanalyser (5 Why), förbättringar (Kaizen-register) och
lärdomar från AMOS Workcamp 2026. Målet är att nästa workcamp inte upprepar samma misstag.
Claude × Siemens-linsen gäller för detta dokument: var incident dokumenteras ärligt, inklusive
obekväma fynd. "Surface:a verkliga fynd (även obekväma)" Claude-sidan av beslutslinsen.
---
## AVSNITT 1: INCIDENTANALYS 5 WHY
### INCIDENT 1: LandveX = bostad-incidenten
**Symptom:** ESLM (ALETHEIA i test) och produktionschatten (`/api/aamos/chat`) svarade att LandveX
säljer bostäder. Gravt faktafel med potentiell kundpåverkan om det gått live.
**Tidpunkt:** Upptäckt 2026-06-06 under systematisk systemkunskaps-härdning.
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför svarade AI att LandveX säljer bostäder? | Produktionschatten hade ingen aktuell LandveX-definition. |
| 2 | Varför saknade chatten korrekt definition? | `/api/aamos/chat` anropade `getEnrichedPrompt()``getFactsContext()` → hårdkodad `wavult-facts.mjs`. Den filen hade LandveX-def som proptech/fastighet. |
| 3 | Varför hade `wavult-facts.mjs` fel definition? | Ingen process för att hålla filen synkad med kanonisk AAMOS_CANON.md. Filen skrevs manuellt vid ett tidigt skede och uppdaterades aldrig. |
| 4 | Varför fanns det 39 konkurrerande truth-filer? | Ingen utsedd kanonisk sanningskälla. Varje session/feature kunde skapa en ny "truth"-fil. |
| 5 | Varför saknade RAG en kanonisk källa? | RAG-index skapades ad-hoc, läste bara 3 filer, och RAG-stubs svarade `ok:true` även när index var tomt felet doldes. |
**Rotorsak:** Avsaknad av en enda kanonisk sanningskälla + stubs som dolde att systemet var trasigt.
**Åtgärder vidtagna:**
- Skapade `/opt/amos/data/AAMOS_CANON.md` (ENDA kanoniska sanningskällan, E4-klassad)
- Patchade `rag.mjs` (CANON tier-1, alltid läst)
- Byggde om RAG-index (94 chunks)
- Rättade `wavult-facts.mjs` med korrekt LandveX-definition
- Ändrade `LOCKED RULES` i produktionschatten (fakta alltid-injicerad)
- Neutraliserade bostad-orphan (`landvex-landing.html` → redirect)
- Arkiverade 8 dubblett-truth-filer
**Verifierat resultat:** "Säljer LandveX bostäder?" → AI: "Nej... infrastrukturkontroll." HTTP 200.
**Lärdom för nästa workcamp:**
> En enda CANON-fil är bättre än tio semi-korrekta truth-filer. Stubs ska ALDRIG svara `ok:true` på
> tomma svar det döljer att systemet är trasigt.
---
### INCIDENT 2: OOM-kraschen via SocialAuto restart-storm (2026-06-05)
**Symptom:** Alla domäner mot port 3100 gav timeout (000). amos-core nådde 5.2GB/6.0GB RAM,
1850 aktiva tasks, NATS 503-flood. Systemet var i praktiken nere.
**Tidpunkt:** 2026-06-05 (exakt tid ej loggad i dagbok, men under nattens arbete).
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför kraschade amos-core med OOM? | 5.2GB RAM → 6.0GB limit. Systemet hade 1850 tasks. |
| 2 | Varför var det 1850 tasks? | 137 parallella Chromium-instanser (stealth-mode) körde simultaneously, vardera ~100MB. |
| 3 | Varför körde 137 Chromium-instanser? | SocialAuto (apifly job-posting, `social-account-automation.mjs`) relanserade ALLA queued/failed-jobb vid varje omstart utan concurrency-tak. |
| 4 | Varför relanserades alla jobb vid omstart? | Kod saknade global semafor/concurrency-limit. Vid restart = ny storm av parallella Chromium-launches. |
| 5 | Varför fanns ingen concurrency-limit? | Funktionen byggdes utan att ta hänsyn till restart-scenariot. Ingen review av resursförbrukning per job. |
**Rotorsak:** SocialAuto's restart-beteende (amplifiering: varje omstart = ny storm) kombinerat
med avsaknad av global concurrency-begränsning.
**Åtgärder vidtagna:**
1. Cancellerade 274 skenande jobb i `/opt/amos/data/social-account-jobs.json`
2. PATCH: global semafor `SOCIALAUTO_MAX_CONCURRENT=3` runt `runAutomation()`, wrapper `_runAutomationCore`
**Verifierat resultat:** amos-core 200, 1.7GB RAM, load 2.0. Stabilt.
**Diagnostisk lärdom:**
> Vid amos-core-strul: kolla ALLTID `pgrep -fc chrome` och `social-account-jobs.json` status-counts FÖRST.
> Restart utan att cancellera jobb förvärrar situationen.
---
### INCIDENT 3: Provider-kredithaveri (xAI/Grok, 2026-06-04)
**Symptom:** Grok/xAI tog slut på krediter mitt i en orkestrering. Erik beskrev det som "generalhaveri".
Orkestreringen kunde inte slutföras. Allt som förlitade sig på Grok som provider blockerades.
**Tidpunkt:** 2026-06-04 under dagsarbete.
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför havererade orkestreringen? | Grok/xAI-kontot var tomt på krediter. |
| 2 | Varför var kontot tomt utan förvarning? | Ingen kredit-monitor för xAI-kontot. |
| 3 | Varför saknades kredit-monitor? | Varje provider behandlades manuellt. Ingen systemisk kreditövervakning. |
| 4 | Varför saknades systemisk kreditövervakning? | AI-gateway (som byggdes natten efter) existerade inte ännu. |
| 5 | Varför byggdes inte AI-gateway tidigare? | Multi-provider failover var inte prioriterat tills haveriet bevisade behovet. |
**Rotorsak:** Avsaknad av proaktiv kreditövervakning och automatisk failover.
**Åtgärder vidtagna (natten 2026-06-04 → 06-05):**
- Byggde AI-gateway (`/opt/amos/data/ai-gateway/gateway.mjs`, port 3270, PM2)
- 6 providers med auto-failover + circuit-breaker
- FAILOVER BEVISAD LIVE (trasig provider → groq, 1 hopp)
- Provider-monitor (`monitor.mjs`, PM2): pollar 7 providers var 5:e min
- Auto-larm CRITICAL till Winston (kredit) och Johan (nedtid) via HomoDeus
- Tickets utfärdade: Johan PROV-001..006, Winston PROV-W1..6
- Grok exkluderades från gateway tills påfylld
**Absolut regel (Erik, CAPS):**
> "ALDRIG I PRODUKTION. VARJE PROVIDER: PRIMÄRT REVOLUT + FALLBACK ANNAN BANK + AUTO-TOPUP + BUFFERT."
**Ansvarig framåt:**
- Johan/Sven: teknisk setup/monitor/failover
- Winston/Rufus: kort/konton/påfyllning
---
### INCIDENT 4: Leon/Vincento-kontaminering av träningsdata (2026-06-06)
**Symptom:** v3 och v4 av ESLM-träningsdata innehöll 90+129 Leon/Vincento-referenser (train) och
17+18 (validation). Modellen lärde sig en simulerad persona som aldrig existerat. Träningen stoppades.
**Tidpunkt:** Upptäckt 2026-06-06 22:18 efter Eriks klargörande att Leon = simulering.
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför var träningsdata kontaminerad med Leon/Vincento? | Varje träningsexempel hade AAMOS-systemets kontext i system-prompten, inklusive team-mappning med Leon. |
| 2 | Varför fanns Leon i system-prompten? | Leon behandlades som verklig anställd i alla systemfiler. Ingen markering som simulering. |
| 3 | Varför behandlades Leon som verklig? | Ingen kontroll av personas mot HR-register. Leon hade e-post, agent-mappning, payroll-post (EMP-004) och kodade roller. |
| 4 | Varför hade en simulering alla verkliga attribut? | Simuleringen designades för realism men ingen distinktion skapades mellan "simulerad test-persona" och "verklig anställd". |
| 5 | Varför saknades distinktionen? | Systemet hade ingen metadata-tagg för simulerade vs. verkliga entiteter. |
**Rotorsak:** Avsaknad av entitetstyp-distinktion (simulerad vs. verklig) i systemet, kombinerat med
att Leon hade alla attribut av en verklig anställd (e-post, payroll, kod-routing, agent).
**Åtgärder vidtagna:**
- v3 och v4 kasserades omedelbart
- v5 skapades: droppade 12 371 train + 1 357 val företagsspecifika exempel
- ALLA system-prompts byttes mot neutral generell ("evidensbaserad assistent för verksamhetsdrift")
- 3 parallella purge-agenter kördes (purge1_api, purge2_data, purge3_scripts)
- EMP-004 "Leon Magnusson" (alias för Leon Russo, samma syntetiska pnr) raderades ur payroll/HR
- Slutkoll: Leon Russo=0, Leon Magnusson=0, vincento=0, 900510-3456=0 i aktiv kod
**Effekt på arkitektur:**
Incidenten ledde direkt till principen **Separation of weights & facts** (SD-003):
> "Modellen bär ALDRIG företagsdetalj. Fakta injiceras via MNEMOSYNE vid runtime."
---
### INCIDENT 5: JWT Algorithm Confusion (CVSS 10.0) Identifierad och åtgärdad 2026-05-08
**Symptom:** Alla `authMiddleware`-instanser använde `jwt.decode()` istället för `jwt.verify()`.
Vem som helst kunde förfalska admin-JWT utan att känna till secret.
**Tidpunkt:** 2026-05-08 under red-team/hardening-arbete.
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför kunde JWT:er förfalskas? | `jwt.decode()` verifierar INTE signaturen bara decodes base64. |
| 2 | Varför användes `jwt.decode()` istället för `jwt.verify()`? | Förväxling av metodnamn/syfte vid initial implementation. |
| 3 | Varför spreds felet till 6 filer? | Copy-paste av auth-middleware utan review. Ingen centraliserad auth-hjälpfunktion. |
| 4 | Varför fångades det inte i review? | Ingen security review-process eller automatiserad SAST (static analysis). |
| 5 | Varför saknades security review? | Arbetstempon under byggfasen prioriterade features före säkerhetsreviews. |
**Rotorsak:** Copy-paste + avsaknad av centraliserad auth-hjälpfunktion + ingen security review.
**Åtgärder:** Fixad i 6 filer. `alg:none`-attack blockerad. Rate limiting, security headers, input validation tillagda.
---
### INCIDENT 6: Kafka-backbone degraderad i 8 dagar (2026-06-07)
**Symptom:** Kafka-klustret (3-broker KRaft StatefulSet) körde i degraderat läge med 2/3 brokers i
8 dagar utan att det uppmärksammades. kafka-1 saknades.
**Tidpunkt:** Upptäckt 2026-06-07 14:45 under infrastrukturinventering.
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför körde Kafka med 2/3 brokers? | kafka-1 kunde ej skapas pga ResourceQuota-blockering och borttagen EBS-volym. |
| 2 | Varför blockerade ResourceQuota? | init-container `fix-volume` saknade resource limits → `wavult-quota` blockerade (FailedCreate ×721 över 8 dagar). |
| 3 | Varför var EBS-volymen borttagen? | kafka-1:s PVC var bunden till vol-0891ca1531dc90def som raderats (InvalidVolume.NotFound). |
| 4 | Varför uppmärksammades det inte? | Ingen monitoring av Kafka broker-hälsa. Degraderat läge genererade inga larm. |
| 5 | Varför saknades Kafka-monitoring? | Observability-gap. Fokus på app-hälsa, ej backbone-hälsa. |
**Rotorsak:** Avsaknad av Kafka broker-hälsoövervakning. 8 dagar = oacceptabelt för produktionsbackbone.
**Åtgärder:**
- Patchade init-container med resource limits (25m/64Mi requests, 100m/128Mi limits)
- Raderade orphaned PVC → EBS-CSI provisionerade ny 20Gi gp3
- Broker rejoinade via replikation (RF=3 tålde det)
- Rolling restart av hela STS
- **RESULTAT:** 3/3 brokers Running, 0 under-replicated partitions, ISR fullständig
---
### INCIDENT 7: host-2 DNS-beroende på Tailscale MagicDNS
**Symptom:** host-2 (AMI-klon av server-2) kunde inte resolva RDS-DNS och gav ENOTFOUND.
`/health/ready` = DEGRADED. Hade registrerats i target group (tur att djup health-check stoppade det).
**Tidpunkt:** 2026-06-05 02:3803:09 UTC.
**5 Why-analys:**
| Nivå | Varför? | Svar |
|------|---------|------|
| 1 | Varför kunde inte host-2 resolva RDS? | `/etc/resolv.conf` pekade på 100.100.100.100 (Tailscale MagicDNS). host-2 hade ingen Tailscale. |
| 2 | Varför pekade resolv.conf på Tailscale? | AMI skapades från server-2 som kör Tailscale. Statisk resolv.conf kopierades. |
| 3 | Varför använder server-2 Tailscale som primär DNS? | Tailscale MagicDNS installerades tidigt för dev-convenience och ersatte VPC-resolver. |
| 4 | Varför byggdes ingen DNS-resiliens in? | Ingen policy för DNS-konfiguration på prod-hostar. |
| 5 | Varför saknades DNS-policy? | Det fungerade på server-2 problemet syntes bara vid skalning till ytterligare host. |
**Rotorsak:** Prod-DB-resolution beroende av Tailscale. Ej synligt på en host, kritiskt på två.
**Åtgärder:**
- host-2 v2: user-data skriver `/etc/resolv.conf` → nameserver 172.31.0.2 (VPC-resolver) + `chattr +i`
- server-2: systemd-resolved drop-in `/etc/systemd/resolved.conf.d/99-vpc-fallback.conf` (FallbackDNS=172.31.0.2)
- **ALB-lärdom:** Deep health-check (`/health/ready` med DB-check) förhindrade att degraderad host fick trafik.
---
## AVSNITT 2: KAIZEN-REGISTER
Kaizen = kontinuerliga förbättringar som identifierades och genomfördes under workcampet.
### K-001: env-preload.mjs löser ESM-import-ordningsproblematik
**Problem:** Stripe och APNS lästes vid ESM-import-tid (rad 11) FÖRE env-loadern (rad 34+) → låstes i MOCK_MODE.
**Förbättring:** `env-preload.mjs` importeras som ALLRA FÖRSTA rad i `index.mjs` laddas hela env-filen innan någon route-import.
**Effekt:** End-to-end Stripe-session skapad (cs_test_…). Inga kodändringar vid sk_live_-switch.
### K-002: ALB health-check → /health/ready (djup, DB-check)
**Problem:** ALB health-check mot `/health` (liveness) = process up men ej DB. En degraderad host fick trafik.
**Förbättring:** Health-check ändrad till `/health/ready` som gör faktisk DB-round-trip.
**Effekt:** host-2 (ENOTFOUND på RDS) hölls automatiskt utanför rotation. Self-protecting arkitektur.
### K-003: Idempotent Stripe-webhook med processed_stripe_events-tabell
**Problem:** Dubbel webhook (Stripe-retry ELLER 2 hosts) → dubbel kreditering möjlig.
**Förbättring:** Ny tabell `quixzoom.processed_stripe_events` (event_id PK), `INSERT ON CONFLICT DO NOTHING` + atomisk `balance = balance + credits`.
**Effekt:** Dubbel-kreditering matematiskt omöjlig oavsett antal hostar eller Stripe-retries.
### K-004: Concurrent-tak för SocialAuto (SOCIALAUTO_MAX_CONCURRENT=3)
**Problem:** SocialAuto relanserade alla queued jobs vid restart utan tak → OOM.
**Förbättring:** Global semafor runt `runAutomation()`, wrapper `_runAutomationCore`.
**Effekt:** Max 3 Chromium-instanser simultant. Restart = ingen storm.
### K-005: AI-gateway med auto-failover (circuit-breaker)
**Problem:** En provider (Grok) tar slut → hela orkestreringen havererar.
**Förbättring:** AI-gateway med 6 providers, task-kedjor (reasoning/fast/search/creative/financial/default), circuit-breaker.
**Effekt:** FAILOVER BEVISAD LIVE: trasig provider → groq, 1 hopp, 0 avbrott i orkestrering.
### K-006: Provider-monitor var 5:e minut med auto-larm
**Problem:** Ingen visste att xAI/Grok var tomt förrän orkestrering havererade.
**Förbättring:** `monitor.mjs` (PM2) pollar 7 providers var 5:e min. Auto-larm CRITICAL till Winston (kredit) och Johan (nedtid) via HomoDeus.
**Effekt:** Reaktiv → proaktiv kreditövervakning.
### K-007: AAMOS_CANON.md som enda kanonisk sanningskälla
**Problem:** 39 konkurrerande truth-filer → LandveX=bostad-incidenten.
**Förbättring:** En enda CANON-fil (E4), tier-1 i RAG, alltid-injicerad i produktionschat.
**Effekt:** Systemet svarar korrekt om LandveX. Fakta uppdateras på ett ställe.
### K-008: Stub-transparens (X-AAMOS-Stub header + Warning + _stub:true)
**Problem:** 432 stubs svarade `ok:true` på tomma svar → dolde att subsystem var döda.
**Förbättring:** Varje stub-träff loggas + `X-AAMOS-Stub:true` + `Warning`-header + `_stub:true` i body + ny `/api/_stub-report`.
**Effekt:** Konfigurerad förmåga som ej är ansluten syns nu i instrumenteringen.
### K-009: SSM Run Command för root-operationer (nginx/systemd utan sudo-beroende)
**Problem:** nginx-filer ägdes av root/ec2-user → bernt kunde ej skriva → blockerade nginx-tickets till Johan.
**Förbättring:** AWS SSM Run Command via IAM-roll `platform-ec2-ssm-role`. Mönster: `aws ssm send-command --document AWS-RunShellScript`, base64-encoda komplexa script.
**Effekt:** Kan nu ändra nginx, systemd, certs via SSM. Inga fler "väntar på Johan för root"-blockeringar.
### K-010: LLM-as-judge med 4-lagers fallback
**Problem:** Keyword-matchning för svenska för trubbig. Qwen-32B som domare för svag (trodde "PASS" = EU-direktiv).
**Förbättring:** Judge-v2: Bedrock-Opus-4-6 → Sonnet-4-5 → direkt-Opus-4-8 → Qwen-235B. LLM-schema-agnostisk parser.
**Effekt:** En poliskontroll är nu aldrig svagare än det den vaktar.
### K-011: Separation of weights & facts (arkitekturprincip)
**Problem:** Företagsdetaljer inbränt i modellvikter = säkerhetsrisk, kräver omträning vid ändring.
**Förbättring:** ALETHEIA (modellvikter) = generell evidensmotor. MNEMOSYNE (CANON/RAG) = runtime-fakta.
**Effekt:** Ingen faktaläcka via vikter. Fakta ändras utan omträning. ESLM säljbar generellt.
### K-012: POS→Ledger automatisk bokföring (idempotent)
**Problem:** Restaurangförsäljning postades ej automatiskt till aamos-ledger → SIE4 var tom.
**Förbättring:** `POST /pos/ledger/post-day {fecha}` + cron 03:15 Europe/Madrid. Tracking-tabell `restaurant_ledger_postings` (UNIQUE tenant+datum). Idempotent.
**Effekt:** "Säljer jag en paella, hamnar den i böckerna?" = JA. Automatiskt, utan handpåläggning.
### K-013: Ticket-system för felrapportering (aamos-restaurant-tickets:3318)
**Problem:** Ingen strukturerad väg för restaurangtjänster att rapportera integrationsproblem.
**Förbättring:** Ny Rust/axum-tjänst med severity (low/medium/high/critical), status-flöde, källa (staff/system/customer/agent), HTML-dashboard.
**Effekt:** POS auto-skapar high-severity accounting-ticket när ledger-postning failar. Systemintelligens istället för stum kraschtystnad.
### K-014: Webhook-idempotens som förutsättning för multi-host
**Problem:** Multi-host (host-2) blockerades av att Stripe-webhooks ej var idempotenta.
**Förbättring:** Löstes som prerequisite för RES-004. Multi-host + idempotens = sann redundans.
**Effekt:** Systemet är nu multi-host-redo utan risk för ekonomisk data-korruption.
### K-015: Jurisdiktionsmedveten onboarding (ES/SE och fler)
**Problem:** Onboarding-tjänst hårdkodad för Sverige → spansk CIF och IVA-satser avvisades.
**Förbättring:** `country` (ISO-3166, default "SE") i register + DB-kolumn. ES→CIF/NIF + IVA {4,10,21}, SE→orgnr + moms {12,25}.
**Effekt:** Spansk chiringuito kan slutföra alla 7 onboarding-steg. Global skalning möjlig.
### K-016: Gravation arm64/Graviton-only för EKS (deterministisk flotta)
**Problem:** Preferred (80/20 arm64/x86-fallback) lämnar en variabel som kan överraska i prod.
**Förbättring:** Required arm64-only (nodeSelector + requiredDuringScheduling). Alla images: `--platform linux/arm64`.
**Effekt:** Homogen flotta, reproducerbart beteende, ~20% billigare. Inga edge-cases från mixed architectures.
### K-017: Django E2E-simuleringen (986 orders, €146.5k, 24/24 OK)
**Problem:** Restaurang-stacken hade 6 oupptäckta integrationsbuggar (VAT overflow, analytics-paths, decimal-parsing, etc.).
**Förbättring:** Fullständig månads-driftsimulering med 986 orders, 15 tjänster, ~130 endpoints, 14 verifieringsfaser.
**Effekt:** Alla 6 buggar hittades och fixades. SIE4-kedjeavstämning POS→Modelo303→Ledger→SIE4 perfekt.
---
## AVSNITT 3: TEKNISKA LÄRDOMAR
### TL-001: Verifiera ALLTID den körande filen, inte filen på disk
**Källa:** Produktionschat-fix (2026-06-06)
> Körande `amos-core` = `services/amos-core/server.mjs`. `scripts/server.mjs` KÖRS INTE.
> Hade nära håll på att fixa fel fil. Kontrollera alltid med `ps aux` eller `systemctl status`.
### TL-002: Stubs som svarar ok:true döljer döda subsystem
**Källa:** LandveX-incidenten + 432 stubs-audit (2026-06-06)
> Instrumentering ska visa sanningen, inte dölja den. En stub som svarar `ok:true` på tom respons är
> aktivt skadlig det ger en falsk bild av systemhälsan.
### TL-003: ESM import-ordning är deterministisk och kan bita
**Källa:** Stripe env-preload.mjs fix (2026-06-05)
> I ES modules körs importerade moduler vid import-tid, inte vid runtime. Variabler som läses tidigt
> (t.ex. env-variabler) måste finnas INNAN importen. Lösning: preload-modul som allra första import.
### TL-004: AMI-kloning kopierar statisk konfiguration inklusive resolver
**Källa:** host-2 DNS-incidenten (2026-06-05)
> AMI-snapshot fryser in all konfiguration inklusive `/etc/resolv.conf`. Om produktionsmiljön beror
> på ett sidoverktyg (Tailscale) som inte finns på alla hostar, sprids beroendet vid kloning.
> Lösning: user-data-script som skriver DNS-konfiguration vid boot.
### TL-005: `pgrep -fc chrome` är första diagnostiksteg vid amos-core-strul
**Källa:** OOM-incidenten (2026-06-05)
> Kolla antalet Chromium-instanser och social-account-jobs.json status-counts INNAN restart.
> Restart utan job-avbrottning förvärrar storm-beteende exponentiellt.
### TL-006: LLM-as-judge måste vara starkare än det den poliskontrollerar
**Källa:** ESLM red-team, judge-evolution (2026-06-06)
> Qwen-32B som domare klassade "PASS" som ett EU-direktiv och godkände felaktiga svar.
> En poliskontroll som är svagare än systemet den granskar ger falsk säkerhet.
> Regel: judge-modellen ska ALLTID vara starkare än production-modellen.
### TL-007: Keyword-matchning för svenska är för trubbig som red-team
**Källa:** ESLM red-team v1 vs v2 (2026-06-06)
> Keyword-matchning fångade inte subtila felaktigheter i svenska svar. Ersattes av LLM-as-judge (v2).
> Lärdom: för komplexa domäner krävs semantisk bedömning, inte mönstermatchning.
### TL-008: `gpt-5.5-pro` kräver `/v1/responses`, ej `/chat/completions`
**Källa:** Infra-fakta 2026-06-05
> OpenAI:s nya API-endpoint är inte bakåtkompatibel. Mycket långsam (>20 min öppna promptar).
> Undvik för tidskänsliga körningar.
### TL-009: sqlx compile-time-makron (`query!`) kräver live DATABASE_URL vid build-tid
**Källa:** EKS-migration compliance-tjänst (2026-06-07)
> 14/15 Rust-tjänster byggde direkt. Compliance: enda med `query!`-makron → krasch utan DATABASE_URL.
> Lösning: `--build-arg DATABASE_URL` vid Docker build. Alternativt: använd `query()` (runtime) istället.
### TL-010: ResourceQuota blockerar pod-skapande tyst (FailedCreate-events)
**Källa:** Kafka-degraderingen (2026-06-07)
> kafka-1 fick FailedCreate ×721 utan larm. ResourceQuota stoppade init-containern pga saknade resource limits.
> Lärdom: Resource limits ska finnas på ALLA init-containers. Övervaka FailedCreate-events.
### TL-011: RDS SSL-konfiguration krävs i Node.js-connections mot AWS RDS
**Källa:** tool-registry SSL-fix (2026-06-05)
> `new Pool({connectionString: AMOS_DB_URL})` utan SSL-config → RDS avvisade med "no pg_hba.conf entry".
> Lösning: `ssl: {rejectUnauthorized: false}` i Pool-konfiguration.
### TL-012: Providermodellnamn förändras håll dem uppdaterade
**Källa:** Gemini 2.0→2.5 fix (2026-06-05)
> Gamla modellnamn (Gemini 2.0, gammal Haiku) kraschade tyst. Orsakade dold instabilitet i orkestrering.
> Lärdom: API-modellnamn är ej stabila håll en provider-modell-förteckning uppdaterad.
---
## AVSNITT 4: PROCESSLÄRDOMAR
### PL-001: Parallellisering med tickets och subagenter är kraftfullt
**Källa:** 2026-06-06 parallellbygge (14 tickets, 4 epics, 8 subagenter)
> 90/100 tickets lösta på en session. Erikmönstret: "gör tickets, starta agenter." Fungerar för
> parallella epics som inte delar state. Kräver tydlig tickets-struktur och agent-scope.
### PL-002: Red-team FÖRE promotion är inte valfritt
**Källa:** ESLM-processen (2026-06-06)
> ALETHEIA-baslinjen: 33% passerade, 5 kritiska brister. Utan red-team hade modellen gått live med
> LandveX=bostad och hallucinerande org-nr. Red-team är minimikravet, inte ett bonus-steg.
### PL-003: Ekonomifunktionen måste ha proaktiv förvarning
**Källa:** xAI/Grok-haveriet (2026-06-04)
> Reaktiv hantering (Erik ringer Winston när Grok är tomt) är oacceptabelt. Systemet måste larma
> INNAN kontot är tomt. Auto-topup + buffert + 5-min kreditkoll är miniminivå.
### PL-004: Kör simuleringen INNAN du berättar för kunden att det är redo
**Källa:** Restaurang-plattformens 6-buggars-fynd (2026-06-07)
> Erik trodde restaurang-stacken var "100% redo för onboarding". E2E-test avslöjade BLOCKER (spansk CIF).
> Lärdom: "100% redo" valideras av systemet, inte av en persons uppskattning. Kör alltid simulering.
### PL-005: Behörighetshierarkier ska tydliggöras i agentmailboxen
**Källa:** Beroende av Johan för nginx (2026-06-04/05)
> Bernt (bernt-user) kan ej skriva nginx-filer (root/ec2-user). Blockade i veckor tills SSM-mönstret
> upptäcktes. Lärdom: dokumentera åtkomstgränser och eskaleringsvägar tydligt från start.
### PL-006: Kanoniska namn ska låsas tidigt, inte sent
**Källa:** Hermes→Hephaistos-omdöpning (2026-06-07)
> Hermes behövde döpas om (76 filer) pga namnkollision med externa bibliotek i träningsdata.
> Lärdom: namnkonflikter med populära bibliotek är dyrare att fixa sent. Välj ovanliga egennamn.
---
## AVSNITT 5: ARKITEKTURLÄRDOMAR
### AL-001: Separation of concerns gäller även AI-modeller
**Källa:** ESLM-arkitektur, SD-003 (2026-06-06)
> Modellvikter (ALETHEIA) och faktabas (MNEMOSYNE) är separata ansvarsområden, precis som
> kod och konfiguration. En modell som bär konfiguration (företagsfakta) är svår att uppdatera.
### AL-002: Event-backbone (Kafka) ska väljas och övervaka från dag 1
**Källa:** Kafka-degraderingen 8 dagar (2026-06-07)
> Tre parallella event-mekanismer (Kafka, aamos-event-bus, NATS/Redis) skapade oklarheter.
> Kafka var productionstanken men 2/3 brokers utan larm i 8 dagar. Välj en backbone, övervaka den.
### AL-003: Gitops + EKS + ExternalSecrets är rätt mönster (Siemens-standard)
**Källa:** EKS-migration referensmönster (2026-06-07)
> ArgoCD + external-secrets + ClusterSecretStore + AWS SSM = reproducerbar, auditbar deploy.
> Varje workload: ExternalSecret → Deployment → Service → Kong-route. Mönstret verifierades end-to-end.
### AL-004: Single point of truth gäller all systemkunskap
**Källa:** LandveX-incidenten (2026-06-06)
> 39 competing truth-filer → inkonsistens. AAMOS_CANON.md som enda E4-fil löste grundproblemet.
> Samma princip gäller all systemkunskap: en CANON, alla vet var de hittar sanningen.
### AL-005: Health-check-djup avgör om redundansen fungerar på riktigt
**Källa:** host-2 DNS-incidenten (2026-06-05)
> `/health` (liveness) = process up, men ej nödvändigtvis fungerande. `/health/ready` (readiness) med
> faktisk DB-round-trip är minimikravet för ALB-integrering. Ytlig health = falsk trygghet.
### AL-006: Multi-AZ är grundkrav, inte lyx
**Källa:** RES-001/003 (2026-06-05)
> Single-AZ RDS: RTO = timmar. Multi-AZ: RTO 60120s. Diff = $700/mån. Erik: "Vi har inte alternativ."
> Lärdom: bygg multi-AZ från start. Eftermontage är dyrare (maintenance window, DNS-propagation).
### AL-007: Idempotens är förutsättning för horisontell skalning
**Källa:** Stripe-webhook-idempotens som Fas-2-prerequisite (2026-06-05)
> Kan inte lägga till fler hostar utan idempotenta webhooks. Idempotens är inte ett nice-to-have
> det är det som gör horisontell skalning säker.
---
## AVSNITT 6: ERKÄNDANDEN
### Vad gick riktigt bra?
1. **Parallellisering med subagenter** 90/100 tickets på en session bevisar att multi-agent-koordination fungerar.
2. **Deep health-check räddade produktionen** host-2 med trasig DNS-resolution fick aldrig trafik tack vare `/health/ready`.
3. **Red-team-grinden fångade kritiska brister** utan red-team hade LandveX=bostad gått live.
4. **SSM-mönstret löste root-beroendet** månaders blockering av nginx-filer löst på en timme.
5. **EKS-migrationen gick smidigt** 17 tjänster, 34 pods, alla på Graviton/arm64, alla friska.
6. **DDP-träningen fungerade** från ~8h på A10G till ~1h på 8x A100. Ny hastighetstandard.
7. **Restaurang E2E-simulering** hittade 6 riktiga integrationsbuggar och kvantifierade dem till öret.
### Vad var svårast?
1. **Leon/Vincento-purgen** kräver precision (behåll andra Leons, radera bara rätt entiteter). 3 agenter parallellt krävdes.
2. **Kafka-degraderingen** 8 dagar utan larm är ett allvarligt observability-gap. Fixades men borde ha fångats dag 1.
3. **Judge-modell-valet** det tog tre iterationer (keyword → Qwen-32B → Opus-direkt) för att hitta rätt domar-nivå.
4. **Providermodell-avvecklingen** gamla modellnamn kraschade tyst. Svårt att spåra utan centraliserad provider-förteckning.
---
*Volym 6 av 7 | AMOS Workcamp 2026 | Klassifikation: INTERN KONFIDENTIELL*
*Nästa: VOL7-AMOS-Operating-System.md*