# Prüfprotokoll INT-12 — AI-Anbindung: Objekttyp-Registrierung (Kopplung AI GOV-01) Datum: 2026-10-15 Host: 192.168.1.204 (Wegwerf-Arbeitsverzeichnis) Branch: `feature/int-12-ai-anbindung-objekttyp-registrierung-kopplung-ai-gov-01` ## Ausgangslage - **DOC-01** (`dms/internal/upload`) liefert Upload/Chunk-Handling, aus dem die KI-relevanten Dokumentfelder stammen (Volltext aus OCR/SRC-05, Kernmetadaten aus MET-02/DOC-01). - **GOV-01** (`ai/internal/moduletype`) existierte bereits als reines Go-Paket mit `Registry.Register`, `Get`, `ListByModule` gegen die Tabelle `module_objecttypes`, inkl. Tenant-Verifikation gegen Core TEN-01 (GOV-07, fail-closed) über das `TenantVerifier`-Interface. - Es gab **keinen HTTP-Endpunkt** für diese Registrierung — DMS ist ein physisch getrenntes Go-Modul (`gitea.perlbach24.de/scripte/nexarch/dms`) und kann `ai/internal/moduletype` nicht importieren. Diese Lücke musste als Teil von INT-12 geschlossen werden (minimal, im bestehenden Baustil von `extractapi`/`residencyapi`). ## Umsetzung ### AI-Modul (ai/) - Neu: `ai/internal/moduletypeapi` — HTTP-Bindung für `moduletype.Registry`: - `POST /module-objecttypes` (Service-Token-Pflicht über `X-Service-Token`, fail closed wie extractapi/residencyapi). - Antwort `403` bei `moduletype.ErrUnknownTenant` (GOV-07), `400` bei sonstigen Validierungsfehlern, `200` bei Erfolg. - `ai/cmd/ai-app/main.go`: neue Konfiguration `NEXARCH_AI_APP_CORE_BASE_URL` / `NEXARCH_AI_APP_CORE_SERVICE_TOKEN`. Der Endpunkt wird **nur verdrahtet, wenn eine Core-Basis-URL konfiguriert ist** — ohne Tenant-Verifikationsmöglichkeit bleibt die Registrierungsroute unverdrahtet statt eine Registrierung ohne GOV-07-Prüfung zuzulassen (fail-closed-Grundsatz von GOV-01/GOV-07 bleibt gewahrt). ### DMS-Modul (dms/) - Neu: `dms/internal/moduletypeclient` — schlanker HTTP-Client (Muster wie `dms/internal/retentionclient` für Archive RET-05/DOC-16): - `Register(ctx, tenantSlug, objectType, fields)` → `POST {baseURL}/module-objecttypes` mit `X-Service-Token`. - `DocumentFields()` liefert die KI-relevanten Felder des Objekttyps `document`: `fulltext`, `title`, `filename`, `mime_type`, `created_at`, `tags`. - `dms/cmd/app/main.go`: neue Funktion `setupAIObjectTypeRegistration()`, aufgerufen beim Start aus `main()`: - **AK2 (optional/kein Fehler ohne AI-Modul):** fehlt `NEXARCH_DMS_APP_AI_BASE_URL` (stellvertretend für "AI-Modul/Core-LIC-02 nicht aktiviert" — Ops setzt diese URL nur, wenn das AI-Modul lizenziert/aktiv ist, analog zum bestehenden RET-05/DOC-16-Muster in derselben Datei), wird die Registrierung übersprungen, nur eine Logzeile, kein Fehler, kein `log.Fatal`. - **AK3 (fail open bei Registrierungsfehlern):** jeder Fehler aus `Register` (Transport, Timeout, Nicht-200) wird nur geloggt (`log.Printf`), die Funktion kehrt zurück, `main()` läuft unverändert weiter — kein `log.Fatal`, kein Blockieren. - **AK1:** bei erfolgreicher Konfiguration wird der Objekttyp `document` mit den KI-relevanten Feldern automatisch beim Start registriert. ### Bewusst nicht umgesetzt (Scope-Entscheidung) - Keine Deregistrierungs-/Lösch-Schnittstelle: Die Akzeptanzkriterien von INT-12 fordern nur die Registrierung beim Start; der Ticket-Hinweis zu "Löschfehler dürfen anders behandelt werden" beschreibt nur eine Abgrenzung zur Fail-Open-Regel, verlangt aber keine Deregistrierungsfunktion. Um nicht über das Ticket hinaus zu bauen, wurde keine Delete-Route/-Client ergänzt. - Kein eigener HTTP-Client für Core LIC-02 (Feature-Flag-Abfrage) auf DMS-Seite: Ein solcher modulübergreifender Flag-Abfrage-Client existiert im Code noch nirgends (auch nicht in anderen separaten Go-Modulen wie `mail`/`connect`/`workflow`). Stattdessen wird das etablierte, bereits im Code vorhandene Muster (RET-05/DOC-16 in `dms/cmd/app/main.go`) übernommen: Aktivierung/Deaktivierung wird über das Vorhandensein der Basis-URL-Umgebungsvariable abgebildet, die Ops nur setzt, wenn das Modul lizenziert ist. Diese Design-Entscheidung ist dokumentiert, falls ein künftiges Ticket einen generischen LIC-02-HTTP-Client für module-übergreifende Flag-Abfragen einführt. ## Automatisierte Tests (real ausgeführt) Testdatenbank (Wegwerf, eigens angelegt, nicht `dms_dev`/`archivdms`): - Rolle/DB `int12_ai_test` (Postgres 17, lokal auf 204). ### AK1 — Registrierung erscheint bei AI - `ai/internal/moduletypeapi/moduletypeapi_test.go::TestHandleRegister_ValidRequest_Registered` — POST gegen echten `httptest`-Server + echte Postgres-DB, Nachweis per `Registry.Get`. - `dms/internal/moduletypeclient/moduletypeclient_test.go::TestRegister_SendsExpectedPayloadAndAuth` — Client sendet korrektes Payload/Token. - **Realer End-to-End-Nachweis (Prüfung 1, siehe unten):** echter `ai-app`-Prozess + echter DMS-Client-Aufruf, Ergebnis per direkter SQL-Abfrage verifiziert. ### AK2 — AI-Modul nicht aktiviert → DMS startet unverändert - `dms/cmd/app/aiobjecttype_test.go::TestSetupAIObjectTypeRegistration_NoBaseURL_SkipsWithoutPanic` — keine `NEXARCH_DMS_APP_AI_BASE_URL`, Aufruf kehrt ohne Fehler zurück. - `TestSetupAIObjectTypeRegistration_NoTenantSlug_SkipsWithoutPanic` — zusätzliche Absicherung (fehlender Tenant-Kontext). ### AK3 — Registrierungsfehler blockieren DMS-Start nie - `dms/cmd/app/aiobjecttype_test.go::TestSetupAIObjectTypeRegistration_UnreachableEndpoint_DoesNotBlock` — Basis-URL zeigt auf geschlossenen Port (`127.0.0.1:1`), Aufruf kehrt kontrolliert mit Logzeile zurück statt zu blockieren/abzustürzen. - `dms/internal/moduletypeclient/moduletypeclient_test.go::TestRegister_UnreachableEndpoint_IsError` / `TestRegister_NonOKStatus_IsError` — Client liefert Fehler statt stillschweigend zu akzeptieren (Aufrufer entscheidet über Fail-Open). - `ai/internal/moduletypeapi/moduletypeapi_test.go::TestHandleRegister_MissingServiceToken_Rejected`, `TestHandleRegister_UnknownTenant_Rejected` — AI-seitiges fail-closed bei Auth/Tenant bleibt erhalten. Alle genannten Tests real ausgeführt und grün: ``` ok ai/internal/moduletypeapi 0.539s (3 Tests) ok ai/internal/moduletype (unverändert grün) ok ai/cmd/ai-app (unverändert grün) ok dms/internal/moduletypeclient (3 Tests) ok dms/cmd/app (alle Tests inkl. 3 neue INT-12-Tests) ``` Vollständiger `go test ./...` des `ai`-Moduls (alle ~50 Pakete) ebenfalls grün mit `TEST_TENANT_DSN` gegen `int12_ai_test`. ## Prüfungen vor Abnahme (real durchgeführt, nicht nur behauptet) ### Prüfung 1 — Registrierung gegen echte, laufende AI-Testinstanz 1. Migrationen auf `int12_ai_test` angewendet (`ai/internal/migrate`, 16 Migrationen inkl. `module_objecttypes`). 2. Core-TEN-01-Stub gestartet (`127.0.0.1:19901`): `GET /tenants/mandant-int12` → 200, alles andere → 404 (einfacher, legitimer Stub für die fremde, bereits eigenständig vertragsgetestete Core-Schnittstelle — kein DB-Mocking). 3. Echter `ai-app`-Prozess gestartet (`go build` → Binary, `127.0.0.1:19902`), verbunden mit `int12_ai_test` und dem Core-Stub, `NEXARCH_AI_APP_CORE_BASE_URL` gesetzt → Route `/module-objecttypes` ist verdrahtet. 4. Echter DMS-Client (`moduletypeclient.Register`) rief `http://127.0.0.1:19902/module-objecttypes` auf → Antwort `200 ok`. 5. Direkte SQL-Abfrage gegen `int12_ai_test`: ``` select tenant_slug, module_name, object_type, fields from module_objecttypes where tenant_slug='mandant-int12'; ``` Ergebnis: ein Datensatz `dms/document` mit allen 6 KI-relevanten Feldern (`fulltext`, `title`, `filename`, `mime_type`, `created_at`, `tags`) — Objekttyp ist nachweislich bei AI registriert. 6. Alle Testprozesse (ai-app, Core-Stub) anschließend beendet (`pkill`), keine Leichen. ### Prüfung 2 — AI-Modul-Flag deaktiviert - `TestSetupAIObjectTypeRegistration_NoBaseURL_SkipsWithoutPanic`: `NEXARCH_DMS_APP_AI_BASE_URL` leer → `setupAIObjectTypeRegistration()` kehrt sofort zurück, nur eine Logzeile ("deaktiviert"), kein Fehler, kein Prozessabbruch. Entspricht dem Normalbetrieb ohne AI-Modul. ### Prüfung 3 — AI-Endpunkt nicht erreichbar bei Registrierung - `TestSetupAIObjectTypeRegistration_UnreachableEndpoint_DoesNotBlock`: `NEXARCH_DMS_APP_AI_BASE_URL=http://127.0.0.1:1` (verbindungsrefused) → `Register` liefert Fehler, `setupAIObjectTypeRegistration()` loggt ihn und kehrt zurück, `main()` würde unverändert weiterlaufen (kein `log.Fatal` im Pfad). Client-seitig zusätzlich durch `TestRegister_UnreachableEndpoint_IsError` abgesichert (Timeout über `context.WithTimeout(10s)` in `setupAIObjectTypeRegistration`, kein endloses Warten). ## Aufräumen - Testprozesse (ai-app-Binary, Core-Stub) beendet. - Temporäre `cmd/int12-*-tmp`-Verzeichnisse (Migrations-Runner, E2E-Runner) nach Gebrauch wieder gelöscht. - Postgres-Rolle/DB `int12_ai_test` bleibt auf 204 als Wegwerf-Testartefakt stehen (keine Kollision mit `dms_dev`/`archivdms`), kann bei Bedarf gelöscht werden: `DROP DATABASE int12_ai_test; DROP ROLE int12_ai_test;`