# ARC-002 — Capability Registry **Status:** Accepted **Datum:** 2026-06-01 **Beslutsfattare:** AAMOS Architecture Team --- ## Kontext ### Problemet med oreglerad AI-integration AI-komponenter (LLM-anrop, klassificeringsmodeller, anomalidetektion) integreras i AAMOS för att höja kvalitet och automatiseringsgrad. Utan ett centralt register skapas ofrånkomligen: - **Okontrollerade beslutsgränser:** En modul kan börja fatta autonoma beslut utan att det är explicit designat, dokumenterat eller granskat - **AI-drift:** Samma logiska funktion (t.ex. "klassificera kostnad") implementeras på 3 olika sätt i 3 moduler med 3 olika prompter och 3 olika feltoleranser - **Revisionsbrist:** Vid en redovisningsfråga kan vi inte svara på "vilken AI-funktion fattade det här beslutet, med vilket confidence och vilka indata?" - **GDPR-exponering:** Känsliga persondata kan hamna i AI-prompts utan spårbarhet ### Principen: AI föreslår, människa beslutar **I AAMOS är det alltid en människa (eller ett explicit auktoriserat system) som fattar ekonomiska beslut.** AI:n klassificerar, flaggar, rekommenderar och sammanfattar — aldrig bokför, attesterar eller godkänner. Denna princip gäller för alla moduler. Den är inte förhandlingsbar. --- ## Beslut ### 1. Capability Registry som central komponent Alla AI-funktioner i AAMOS **MÅSTE** registreras i Capability Registry innan de exponeras mot en modul. En capability som inte finns i registret får inte anropas i produktion. Registret är källan till sanning för: - Vad AI-systemet **får** göra - Vad AI-systemet **inte får** besluta - Hur AI-beslut ska **loggas och spåras** --- ### 2. Registry-schema ```typescript interface Capability { // Identitet capability_id: string; // Format: "." t.ex. "ekonomi.classification" version: string; // Semver: "1.0.0" name: string; // Läsbart namn: "Kostnadsklassificering" description: string; // Vad funktionen gör, i ett stycke module: string; // Ägande modul: "ekonomi", "crm", "*" (global) status: "active" | "beta" | "deprecated" | "disabled"; // Datakontraktet input_schema: JSONSchema; // Validerar indata till capability output_schema: JSONSchema; // Validerar utdata från capability // Konfidensmodell confidence_model: { type: "binary" | "score" | "categorical" | "none"; threshold_for_auto_action?: number; // 0.0–1.0; null = aldrig auto threshold_for_suggestion?: number; // Under detta: visa men flagga som osäker uncertainty_action: "reject" | "escalate" | "flag"; }; // Beslutsgränser — KRITISKT decision_boundary: { can_auto_execute: boolean; // Får capability köras utan mänsklig granskning? auto_execute_conditions?: string[]; // Om true: exakta villkor som måste gälla max_financial_impact?: number; // Maxbelopp i SEK för auto-action (null = inga finansiella effekter tillåtna) requires_dual_approval?: boolean; // Fyra-ögonprincipen immutable_fields?: string[]; // Fält capability inte får påverka }; // Vem får fatta beslut baserat på denna capability allowed_decision_sources: Array< | "human:any" // Vilken inloggad användare som helst | "human:manager" // Kräver manager-roll | "human:auditor" // Kräver auditor-roll | "system:workflow" // Workflow Engine (med godkänd definition) | "system:scheduler" // Schemalagd process >; // Spårbarhet audit_config: { log_inputs: boolean; // Logga indata (obs: GDPR-krav) log_outputs: boolean; // Logga utdata log_confidence: boolean; // Logga konfidensscore retention_days: number; // Lagringstid för loggar pii_fields: string[]; // Fält som ska redigeras/krypteras i logg }; // Livscykel created_at: string; // ISO 8601 deprecated_at?: string; // När deprecation inleddes sunset_date?: string; // Deadline för borttagning superseded_by?: string; // capability_id för ersättare } ``` --- ### 3. Initiala capabilities #### `ekonomi.classification` ```json { "capability_id": "ekonomi.classification", "version": "1.0.0", "name": "Kostnadsklassificering", "description": "Klassificerar en transaktion eller kostnadsrad mot kontoplan och kostnadsbärare baserat på historik och regelbaserad logik.", "module": "ekonomi", "status": "active", "confidence_model": { "type": "score", "threshold_for_auto_action": null, "threshold_for_suggestion": 0.75, "uncertainty_action": "escalate" }, "decision_boundary": { "can_auto_execute": false, "max_financial_impact": null, "requires_dual_approval": false, "immutable_fields": ["account_code", "amount", "transaction_date"] }, "allowed_decision_sources": ["human:any", "human:manager"] } ``` #### `ekonomi.forecasting` ```json { "capability_id": "ekonomi.forecasting", "version": "1.0.0", "name": "Ekonomisk prognos", "description": "Genererar kassaflödesprognoser och budgetavvikelseprojektioner baserat på historiska transaktionsdata.", "module": "ekonomi", "status": "active", "confidence_model": { "type": "score", "threshold_for_auto_action": null, "threshold_for_suggestion": 0.60, "uncertainty_action": "flag" }, "decision_boundary": { "can_auto_execute": false, "max_financial_impact": null, "requires_dual_approval": false, "immutable_fields": ["*"] }, "allowed_decision_sources": ["human:manager", "human:auditor"] } ``` #### `ekonomi.anomaly_detection` ```json { "capability_id": "ekonomi.anomaly_detection", "version": "1.0.0", "name": "Transaktionsanomalidetektion", "description": "Identifierar avvikande transaktioner som kan indikera fel, dubbletter eller bedrägerimönster. Flaggar för mänsklig granskning.", "module": "ekonomi", "status": "active", "confidence_model": { "type": "score", "threshold_for_auto_action": null, "threshold_for_suggestion": 0.65, "uncertainty_action": "flag" }, "decision_boundary": { "can_auto_execute": false, "max_financial_impact": null, "requires_dual_approval": false, "immutable_fields": ["*"] }, "allowed_decision_sources": ["human:any", "human:auditor"] } ``` #### `ekonomi.recommendation` ```json { "capability_id": "ekonomi.recommendation", "version": "1.0.0", "name": "Åtgärdsrekommendation", "description": "Föreslår nästa steg i ett arbetsflöde — t.ex. betalningsförslag, matchning av faktura mot order, eller kontokorrigeringsförslag. Aldrig auto-exekverande.", "module": "ekonomi", "status": "active", "confidence_model": { "type": "categorical", "threshold_for_auto_action": null, "threshold_for_suggestion": 0.70, "uncertainty_action": "escalate" }, "decision_boundary": { "can_auto_execute": false, "max_financial_impact": null, "requires_dual_approval": false, "immutable_fields": ["amount", "account_code", "transaction_date"] }, "allowed_decision_sources": ["human:any"] } ``` #### `ekonomi.compliance_review` ```json { "capability_id": "ekonomi.compliance_review", "version": "1.0.0", "name": "Regelefterlevnadsgranskning", "description": "Kontrollerar transaktioner och rapporter mot bokföringslagens krav, momskoder och interna kontrollregler. Returnerar en checklista med flaggor.", "module": "ekonomi", "status": "active", "confidence_model": { "type": "binary", "threshold_for_auto_action": null, "threshold_for_suggestion": null, "uncertainty_action": "escalate" }, "decision_boundary": { "can_auto_execute": false, "max_financial_impact": null, "requires_dual_approval": false, "immutable_fields": ["*"] }, "allowed_decision_sources": ["human:auditor", "human:manager"] } ``` #### `ekonomi.document_extraction` ```json { "capability_id": "ekonomi.document_extraction", "version": "1.0.0", "name": "Dokumentdataextraktion", "description": "Extraherar strukturerade data från faktura-PDF:er och kvitton. Returnerar extraherade fält för mänsklig verifiering. Fyller ALDRIG i bokföringssystem autonomt.", "module": "ekonomi", "status": "active", "confidence_model": { "type": "score", "threshold_for_auto_action": null, "threshold_for_suggestion": 0.85, "uncertainty_action": "flag" }, "decision_boundary": { "can_auto_execute": false, "max_financial_impact": null, "requires_dual_approval": false, "immutable_fields": ["account_code", "amount", "vat_amount", "invoice_date"] }, "allowed_decision_sources": ["human:any"] } ``` --- ### 4. Hur en modul konsumerar en capability #### 4.1 API-mönster (REST) ``` POST /api/v1/capabilities/{capability_id}/invoke Authorization: Bearer X-Tenant-ID: X-Correlation-ID: X-Requested-By: { "version": "1.0.0", // Pinnad version (required) "input": { ... }, // Valideras mot capability.input_schema "context": { // Optional: extra kontext för AI "subject_id": "", "subject_type": "Transaction" } } ``` **Svar:** ```json { "capability_id": "ekonomi.classification", "version": "1.0.0", "invocation_id": "", // Unikt anrops-ID för audit "output": { ... }, // Validerat mot capability.output_schema "confidence": 0.91, "decision_required": true, // Alltid true om can_auto_execute=false "suggestion": { ... }, // Presentationsformat för UI "audit_ref": "" // Referens till loggpost } ``` #### 4.2 Konsumtionsmönster i modulkod ```javascript // Rätt mönster const result = await capabilityRegistry.invoke('ekonomi.classification', { version: '1.0.0', input: { transaction_id: txId, description: tx.description, amount: tx.amount }, requestedBy: currentUser.id }); // Presentera förslaget för användaren ui.showSuggestion({ label: 'Föreslaget konto', value: result.output.suggested_account, confidence: result.confidence, invocationId: result.invocation_id // Måste sparas om användaren accepterar }); // ALDRIG detta: // await db.updateTransaction(txId, { account: result.output.suggested_account }); // ↑ AI får inte skriva direkt till bokföring, oavsett confidence ``` #### 4.3 Acceptans-mönster ```javascript // När användaren accepterar ett AI-förslag await capabilityRegistry.recordDecision({ invocation_id: result.invocation_id, decision: 'accepted', // 'accepted' | 'modified' | 'rejected' decided_by: currentUser.id, final_value: chosenAccount, modification_reason: null }); // Sedan applicera beslutet await economyService.postJournalEntry({ ...entry, account: chosenAccount }); ``` --- ### 5. Lifecycle-hantering #### Registrering 1. Modul-team skapar ett PR med ny capability-definition 2. Architecture Team granskar `decision_boundary` och `allowed_decision_sources` 3. Security review om `audit_config.pii_fields` är tomt (alla fält måste granskas) 4. Merged → automatisk registrering vid deploy #### Versionshantering - Bakåtkompatibla ändringar → minor version (`1.0.0 → 1.1.0`) - Breaking changes → major version (`1.0.0 → 2.0.0`) - Gamla versioner hålls aktiva i **90 dagar** efter att ny major-version lanserats - Konsumenter måste upgraderas inom 90 dagars deprecated-period #### Deprecation-flöde ``` status: active → deprecated (sunset_date sätts, superseded_by pekar på ny version) → sunset_date passeras → status: disabled → anrop returnerar 410 Gone med referens till ny version ``` --- ### 6. Enforcement #### Vad händer om en modul försöker fatta beslut utanför sin capability? **Scenario A: Anrop till icke-registrerad capability** ``` HTTP 403 Forbidden { "error": "CAPABILITY_NOT_REGISTERED", "message": "Capability 'ekonomi.auto_post' is not registered in the Capability Registry.", "action_required": "Register the capability before use." } ``` → Loggas som SECURITY_VIOLATION i audit-systemet → Alert till Architecture Team **Scenario B: `can_auto_execute = false` men modulen försöker direktskriva** → Enforcement på applikationsnivå: alla skrivanrop till bokföring kräver ett `invocation_id` som kan spåras till ett mänskligt `recordDecision`-anrop → Om `invocation_id` saknas eller inte är `accepted`: 403 + audit-logg **Scenario C: Capability anropas av ej tillåten decision_source** ``` HTTP 403 Forbidden { "error": "DECISION_SOURCE_NOT_ALLOWED", "capability_id": "ekonomi.compliance_review", "attempted_source": "system:scheduler", "allowed_sources": ["human:auditor", "human:manager"] } ``` **Scenario D: Capability anropas utanför versionens supportfönster** ``` HTTP 410 Gone { "error": "CAPABILITY_VERSION_SUNSET", "superseded_by": "ekonomi.classification@2.0.0" } ``` --- ## Konsekvenser ### Positiva - **Full spårbarhet:** Varje AI-handling kan kopplas till en människa som fattade beslutet - **Revision-ready:** Vid extern revision kan vi visa exakt vilka AI-funktioner använts, av vem, med vilket confidence - **GDPR-kompabilitet:** `pii_fields` i audit_config gör att vi aldrig loggar personnummer etc. i klartext - **Modularitet:** Capabilities kan delas mellan moduler (t.ex. kan CRM använda `document_extraction`) ### Negativa / risker - **Overhead vid ny feature:** Varje ny AI-funktion kräver Architecture Team-granskning → kan upplevas som flaskhals - **Version-pinning-disciplin:** Moduler som inte uppgraderar i tid blockeras av sunset - **Registry som SPOF:** Om registret är nere kan inga AI-anrop göras — kräver hög tillgänglighet och lokal cache-fallback --- ## Implementation ### Fas 1 (Ekonomimodulen, nu) 1. Skapa `capability_registry`-tabell i PostgreSQL 2. Implementera `POST /api/v1/capabilities/{id}/invoke` med schema-validering 3. Registrera de 6 initial-capabilities ovan 4. Lägg till audit-loggning för varje anrop ### Fas 2 (plattformsnivå) 1. Capabilities Registry som standalone-tjänst (`:3260`) 2. gRPC-kontrakt för inter-service-kommunikation 3. Dashboard för capability-översikt och anropsstatistik ### Fas 3 (mognad) 1. Automatisk schema-drift-detektion 2. A/B-versionshantering med gradvis utrullning 3. Federated registry för externa AI-providers --- ## Öppna frågor 1. **Ska capabilities kunna kedjas?** T.ex. `document_extraction` → `classification` som ett pipeline. Kräver att chain-invocation_id loggas. 2. **Hur hanteras LLM-specifik prompt-versionshantering?** Capability-versionen bör inkludera prompt-template-version för reproducerbarhet. 3. **Ska `can_auto_execute: true` någonsin tillåtas?** Möjligt för låg-risk, hög-confidence klassificering av intern data — men kräver separat Architecture Decision och Security Review. 4. **Multi-tenant capability-konfiguration?** En tenant kan behöva inaktivera specifika capabilities (t.ex. av regulatoriska skäl). Registry bör ha tenant-override-stöd.