Files
boc/docs/arc/ARC-002-capability-registry.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

15 KiB
Raw Blame History

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

interface Capability {
  // Identitet
  capability_id: string;           // Format: "<modul>.<funktion>" 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.01.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

{
  "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

{
  "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

{
  "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

{
  "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

{
  "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

{
  "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 <service-token>
X-Tenant-ID: <tenant_id>
X-Correlation-ID: <uuid>
X-Requested-By: <person_id|system_id>

{
  "version": "1.0.0",      // Pinnad version (required)
  "input": { ... },         // Valideras mot capability.input_schema
  "context": {              // Optional: extra kontext för AI
    "subject_id": "<uuid>",
    "subject_type": "Transaction"
  }
}

Svar:

{
  "capability_id": "ekonomi.classification",
  "version": "1.0.0",
  "invocation_id": "<uuid>",  // 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": "<uuid>"        // Referens till loggpost
}

4.2 Konsumtionsmönster i modulkod

// 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

// 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_extractionclassification 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.