p96 follow up

This commit is contained in:
team 1
2026-05-11 15:39:00 +02:00
parent 3e678c659c
commit f098a1a244
25 changed files with 790 additions and 131 deletions

View File

@@ -0,0 +1,200 @@
# RetrieX Patch p95 - Admin Role Matrix Hardening
## Ziel
Dieser Patch gleicht die im Admin sichtbare Rollenlogik mit der serverseitigen Zugriffskontrolle ab. Rollen sollen nicht nur in der Userverwaltung auswählbar sein, sondern an den relevanten Routen, Controllern und UI-Aktionen konsistent wirksam werden.
## Ausgangslage
Nach p93/p94 waren Userverwaltung, Aktiv/Inaktiv-Login-Blocker und Access-Denied-Seiten funktionsfähig. Bei der Rollenprüfung fielen aber mehrere Inkonsistenzen auf:
- `ROLE_EDITOR` existierte in der Hierarchie, wurde aber kaum als fachliche Serverberechtigung genutzt.
- Einige Aktionen waren im UI nur für Super-Admins sichtbar, serverseitig aber nur durch den allgemeinen `/admin`-Zugriff geschützt.
- Indexierungsprofile konnten serverseitig nicht streng genug zwischen Ansicht und kritischen Aktionen unterscheiden.
- `admin_ingest_profile_remove` erlaubte noch `GET` als Methode.
- Sidebar und Dashboard zeigten Links bzw. Aktionen, die für die jeweilige Rolle später nur in 403 endeten.
## Umgesetzte Zielmatrix
| Bereich | Effektive Rolle |
| --- | --- |
| Chat, Ask/SSE, History, Frontend-Messages | `ROLE_CHAT_USER` |
| Admin-Dashboard, Guides, allgemeine Adminbasis, Ingest-Job-Ansicht | `ROLE_ADMIN_AREA` |
| Dokumente, Dokumentversionen, Tags, Tag-Zuweisungen, Dokument-Ingest | `ROLE_EDITOR` |
| Modell-/Retrieval-Konfiguration lesen/testen, Ingest-Profile ansehen | `ROLE_KNOWLEDGE_ADMIN` |
| Userverwaltung, Logs, System Prompt, System Agent, Reset/Delete, Global Reindex, Profil-/Modell-Umschaltungen | `ROLE_SUPER_ADMIN` |
Die bestehende Hierarchie bleibt erhalten:
```yaml
ROLE_SUPER_ADMIN: [ROLE_KNOWLEDGE_ADMIN, ROLE_EDITOR, ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
ROLE_KNOWLEDGE_ADMIN: [ROLE_EDITOR, ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
ROLE_EDITOR: [ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
ROLE_ADMIN_AREA: [ROLE_USER]
ROLE_CHAT_USER: [ROLE_USER]
```
## Geänderte Dateien
### Controller / Security
- `config/packages/security.yaml`
- ergänzt konkrete `access_control`-Regeln vor dem generischen `/admin`-Fallback
- schützt Dokument-/Tag-Bereiche mit `ROLE_EDITOR`
- schützt Modell-/Ingest-Konfigbereiche mit `ROLE_KNOWLEDGE_ADMIN`
- schützt kritische Systembereiche mit `ROLE_SUPER_ADMIN`
- `src/Security/ApplicationRoles.php`
- Dokumentation der zentralen Rollenquelle erweitert
- `src/Controller/Admin/DocumentController.php`
- Dokumentliste/-details und alle Dokumentpflegeaktionen nun `ROLE_EDITOR`
- Reset/Delete bleiben `ROLE_SUPER_ADMIN`
- Rollenstrings auf `ApplicationRoles`-Konstanten umgestellt
- `src/Controller/Admin/DocumentTagController.php`
- Tagbearbeitung auf Dokumentebene nun `ROLE_EDITOR`
- `src/Controller/Admin/TagController.php`
- Tagliste, Taganlage, Löschung und Zuweisung nun `ROLE_EDITOR`
- `src/Controller/Admin/IngestProfileController.php`
- Profilansicht nun `ROLE_KNOWLEDGE_ADMIN`
- Profilanlage, Aktivierung und Löschung nun `ROLE_SUPER_ADMIN`
- `remove` nur noch per `POST`
- Aktivieren/Löschen prüfen jetzt serverseitig CSRF-Token
- `src/Controller/Admin/ModelGenerationConfigController.php`
- `src/Controller/Admin/IngestJobController.php`
- `src/Controller/Admin/AdminVectorLogController.php`
- `src/Controller/Admin/AdminSystemLogController.php`
- `src/Controller/Admin/SystemAgentController.php`
- `src/Controller/Admin/SystemPromptController.php`
- `src/Controller/Admin/UserController.php`
- Rollenstrings auf zentrale `ApplicationRoles`-Konstanten umgestellt
### Templates
- `templates/admin/base.html.twig`
- Sidebar blendet Rollenbereiche passend aus:
- User: Super-Admin
- Dokumente/Tags: Editor
- System Prompt/Agent/Logs: Super-Admin
- KI-/LLM-Setup und Ingest-Profile: Knowledge-Admin
- `templates/admin/dashboard/index.html.twig`
- Global-Reindex-Button nur noch für Super-Admins sichtbar
- Nicht-Super-Admins sehen einen Hinweis statt eines Buttons, der in 403 läuft
- `templates/admin/document/index.html.twig`
- `templates/admin/document/show.html.twig`
- `templates/admin/document/new_version.html.twig`
- Dokumentpflege- und Versionsaktionen auf `ROLE_EDITOR` ausgerichtet
- Dokumentlöschung bleibt `ROLE_SUPER_ADMIN`
- `templates/admin/ingest_profile/list.html.twig`
- Profilanlage nur noch für Super-Admins sichtbar
- Aktivieren/Löschen bleiben Super-Admin-Aktionen
- `templates/admin/tag/index.html.twig`
- Taganlage, Zuweisung und Löschung nur für Editor sichtbar
- Nicht-Editor sehen keine mutierenden Aktionen
## Nicht geändert
- RAG-/Retrieval-Logik
- Scoring
- Shop-Matching
- PromptBuilder
- AgentRunner
- Chat-Antwortlogik
- User-CRUD-Fachlogik aus p93
- Error-Page-/Access-Denied-Grundlogik aus p94
## Lokale Checks
Ausgeführt im ZIP-Stand ohne `vendor/`:
```bash
find src/Controller/Admin src/Security src/Repository src/Service/Admin -type f -name '*.php' -print0 | xargs -0 -n1 php -l
python3 - <<'PY'
from pathlib import Path
import yaml
yaml.safe_load(Path('config/packages/security.yaml').read_text())
print('[OK] yaml parse')
PY
```
Zusätzlich wurde eine einfache Twig-Balance-Prüfung auf `{% if %}`, `{% for %}` und `{% block %}` für die geänderten Templates durchgeführt.
## Empfohlene Checks in der Zielumgebung
```bash
php bin/console cache:clear
php bin/console lint:yaml config/packages/security.yaml
php bin/console lint:twig templates/admin
php bin/console debug:router | grep admin_
php bin/console mto:agent:config:validate
php bin/console mto:agent:regression:test
php bin/console mto:agent:config:audit-source --details
```
## Manuelle Rollentests
### `ROLE_ADMIN_AREA`
Soll können:
- `/admin/dashboard`
- `/admin/guides`
- `/admin/jobs`
Soll verweigert bekommen:
- `/admin/documents`
- `/admin/tags`
- `/admin/model-config`
- `/admin/ingest-profiles`
- `/admin/users`
### `ROLE_EDITOR`
Soll können:
- Dokumente ansehen/anlegen
- Dokumentversionen hochladen
- Versionen aktivieren
- Ingest für Dokumentversionen starten
- Tags anlegen/löschen/zuweisen
Soll verweigert bekommen:
- Userverwaltung
- System Prompt
- System Agent
- Systemlogs / Vectorlog
- Global Reindex
- Dokumentlöschung / kompletter Reset
- Ingest-Profil aktivieren/löschen
### `ROLE_KNOWLEDGE_ADMIN`
Soll können:
- alles aus `ROLE_EDITOR`
- Modellkonfiguration ansehen/testen
- Ingest-Profile ansehen
Soll verweigert bekommen:
- Userverwaltung
- System Prompt/Agent
- Logs
- Profil-/Modell-Aktivierung und Löschung
- Global Reindex
- Reset/Delete
### `ROLE_SUPER_ADMIN`
Soll alles können.

View File

@@ -0,0 +1,197 @@
# RetrieX Patch 96 - Follow-up Product Identity & Weak History Guard
## Ziel
Dieser Patch haertet zwei beobachtete Folgefrage-Fehlerklassen, ohne Retrieval, Scoring, Shop-Ranking oder Shop-Matching fachlich umzubauen:
1. **Preis-Folgeaktionen nach mehreren sichtbaren Produkten** duerfen nicht mehr auf den ersten generischen Modellanker zurueckfallen.
- Fehlerfall: Antwort nennt `Testomat 2000 CLT` und `Testomat LAB CL`; der Button `Preis anzeigen` erzeugte nur `Testomat 2000`.
- Neu: Wenn mehrere in der Antwort sichtbare Produkte erkannt werden, wird die Preis-Folgeaktion aus den konkreten Produktidentitaeten mit Produktnummern gebaut.
2. **Referenzielle Shop-Nachfragen mit schwacher Query** wie `suche im Shop nach der Information` duerfen den Produktfokus aus der History nicht verlieren.
- Fehlerfall: Nach `Testomat 2000 THCL` wurde die Shopquery zu `information` statt zum Verlaufanker `testomat 2000 thcl`.
- Neu: Schwache referenzielle Shopqueries, die nur aus Meta-/Info-/Noise-Tokens bestehen, werden mit dem letzten Produktmodellanker aus der History ersetzt.
## Geaenderte Dateien
- `src/Agent/AgentRunner.php`
- `config/retriex/chat-messages.yaml`
- `config/retriex/genre.yaml`
## Technische Umsetzung
### 1. Preis-Folgeaktionen behalten mehrere konkrete Produktidentitaeten
`buildFollowUpActionPriceQuery()` faellt bei mehreren angezeigten Produkten nicht mehr auf `answer_anchor` zurueck. Stattdessen wird eine begrenzte Produktliste aus den sichtbaren Shopprodukten gebaut, inklusive Produktnummern.
Beispiel erwarteter Button-Prompt:
```text
Zeige mir die Preise zu folgendem Produkt bzw. den Produkten: Testomat 2000® CLT 100137; Testomat® LAB CL 116106.
```
Der Prompt enthaelt bewusst `Produkt/Produkten` und `Preise`, damit die bestehende Product-List-Follow-up-Logik fuer mehrere Produkte greifen kann und einzelne Produktanker separat suchen kann.
### 2. Schwache referenzielle Shopqueries nutzen History-Modellanker
Neue Guard-Funktion:
```php
guardWeakReferentialShopQueryWithHistoryModelAnchor()
```
Sie greift nur, wenn:
- die aktuelle Frage referenziell genug ist, um History fuer Shopqueries zu nutzen,
- die Shopquery keinen eigenen Produktmodellanker enthaelt,
- der Prompt selbst keinen neuen Produktmodellanker enthaelt,
- es kein Product-List-Follow-up ist,
- es kein Indikator-/Zubehoer-/Reagenz-Follow-up ist,
- die Query nur aus schwachen Meta-/Info-/Noise-Tokens besteht,
- in der History ein Produktmodellanker gefunden wird.
Die schwachen Info-Woerter werden in `genre.yaml` gepflegt:
- `information`
- `informationen`
- `info`
- `infos`
- `details`
- `detail`
- `daten`
- `angaben`
## Bewusst nicht geaendert
- Keine neue fachliche Produktliste im PHP-Core.
- Keine neue Testomat-/THCL-/CLT-/LAB-CL-Sonderlogik im Core.
- Keine Aenderung an Retrieval, Scoring, Ranking, Shopware-Suche oder PromptBuilder-Faktenregeln.
- Bestehende Indikator-/Zubehoer-Follow-up-Guards bleiben priorisiert.
## Lokale Checks
Im Patch-Arbeitsverzeichnis ausgefuehrt:
```bash
php -l src/Agent/AgentRunner.php
python3 - <<'PY'
import yaml, pathlib
for path in pathlib.Path('config/retriex').glob('*.yaml'):
with path.open(encoding='utf-8') as f:
yaml.safe_load(f)
print('all retriex yaml OK')
PY
```
Ergebnis:
- `AgentRunner.php`: Syntax OK
- alle `config/retriex/*.yaml`: YAML OK
Nicht lokal ausfuehrbar, weil `vendor/` im ZIP nicht enthalten ist:
```bash
bin/console mto:agent:config:validate
bin/console mto:agent:regression:test
bin/console mto:agent:config:audit-source --details
bin/console mto:agent:config:audit-patterns --details
```
## Testprompts nach Einspielen
### Fehlerfall 1: Preis-Folgeaktion nach zwei Chlor-Geraeten
```text
mit welchem testomat kann ich freies chlor messen
```
Erwartung in der Antwort:
- Hauptprodukte bleiben fokussiert auf die passenden Chlor-Geraete, insbesondere:
- `Testomat 2000® CLT` / `100137`
- `Testomat® LAB CL` / `116106`
- Folgeaktion `Preis anzeigen` darf nicht mehr nur `Testomat 2000` materialisieren.
Danach den angezeigten Preis-Button klicken oder sinngleich testen:
```text
Zeige mir die Preise zu folgendem Produkt bzw. den Produkten: Testomat 2000® CLT 100137; Testomat® LAB CL 116106.
```
Erwartung:
- Shopquery/Split-Lookups fokussieren die konkreten Produktidentitaeten.
- Es duerfen nicht breit alle `Testomat 2000` Varianten wie Fe, Antox, Br2, ClO2 oder Wartungskoffer als Hauptantwort erscheinen.
### Fehlerfall 2: schwache Info-Shopnachfrage verliert THCL-Anker
```text
welche grenzwerte kann der testomat 2000 thcl messen
```
Dann:
```text
suche im shop nach der information
```
Optional auch Tippfehler-Variante:
```text
such eim shop nach der information
```
Erwartung:
- Die finale Shopquery darf nicht `information` sein.
- Erwarteter Verlaufanker: `testomat 2000 thcl`.
- Shopantwort bleibt beim Geraet/Modellfokus und driftet nicht zu fremden Testomat-808-SiO2-Zubehoerteilen, Horiba-Testern oder allgemeinen Treffern ab.
### Gruener Gegenflow: Brauwasser bleibt stabil
```text
ich suche ein wasseranalyse messgerät für eine Brauerei, um das brauwasser messen zu können
```
Dann:
```text
begründe deine auswahl
```
Dann:
```text
was kostet das gerät denn
```
Erwartung:
- Auswahl bleibt auf `Testomat 2000® CAL` fokussiert, sofern die Begruendung CAL als Hauptempfehlung gesetzt hat.
- Preis-Follow-up bleibt `testomat 2000 cal gerät` bzw. entsprechend CAL-fokussiert.
- Keine Regression hin zu breiten `testomat 2000` Varianten.
### Bestehende Praezisionsregression: Indikator 300 bleibt stabil
```text
Was ist der niedrigste Grenzwert für die Wasserhärte, welcher mit einem Testomaten überwacht werden kann?
```
Dann:
```text
mit welchem indikator wird der wert gemessen
```
Dann:
```text
was kostet der indikator
```
Erwartung:
- `0,02 °dH` / `Testomat 808`
- `Indikatortyp 300`
- Shopquery weiterhin fokussiert, z. B. `testomat 808 300 indikator`
- Nur die zwei exakten Indikator-300-Produkte, keine `300 S`-Varianten und keine anderen Codes.