# Projekt-Recherche v1 `ProjectResearch` ist die gemeinsame lesende Schnittstelle für die deterministische Case-Aufbereitung und die LLM-gesteuerte Hypothesenuntersuchung. Die erste LLM-Phase bleibt der CaseInterpreter. Der CaseBuilder diagnostiziert nicht und führt keine LLM-Abfragen aus. `docs/architecture.md` bleibt unverändert. ## Datenquellen und Grenzen - Sourcecode: lokaler Git-Clone des vom Betreiber freigegebenen Repositories. Abfragen lesen Git-Objekte eines vollständigen Commit-Hashes, nicht den veränderlichen Arbeitsbaum. Ein Branchwechsel oder Git-Pull ändert keine bestehende Bindung. - Source-Schemata: SQL-Dateien desselben Commits. Sie beweisen Tabellenstrukturen dieses Codestands, keine tatsächlich vorhandenen Datenbankzeilen. - Datenbank: parameterisierte SELECT-Abfragen gegen ausdrücklich freigegebene Tabellen einer MySQL-/MariaDB-Datenbank. Zugangsdaten bleiben in Environment-Variablen oder einer lokalen Passwortdatei. Sie werden nicht in Requests, Captures oder Logs kopiert. - Externe Webrecherche: weiterhin separat für vorgesehenes Verhalten. Dieser Meilenstein aktiviert keine Google-Suche und führt keine kostenpflichtigen LLM-Aufrufe aus. Die LLM darf weder Serveradresse, URL, Branch, Dateisystemwurzel, Datenbank noch Zugangsdaten bestimmen. Freies SQL, Shellzugriff, Joins mit frei vorgegebenen Ausdrücken, Schreiboperationen, Builds und Labsteuerung stehen nicht zur Verfügung. Zusammenhänge werden durch aufeinanderfolgende explizite Abfragen untersucht. ## Operationen Die Investigation verwendet `next.kind = "tool"`, `operation = "project.query"` und `arguments` gemäß `project-query-v1.schema.json`. | `arguments.kind` | Funktion | | --- | --- | | `source.files` | Dateipfade durch literalen Teilstring finden | | `source.search` | Literale Textsuche, optional auf Pfadpräfix begrenzt | | `source.symbol` | Suche nach vollständigem Symbolwort | | `source.registration` | Symbolvorkommen als Ausgangspunkt einer Registrierungsprüfung | | `source.schema` | Literale Suche ausschließlich in SQL-Dateien | | `source.read` | Begrenzten Zeilenbereich einer bekannten Datei lesen | | `database.tables` | Namen in den konfigurierten Datenbanken und Tabellenfreigaben finden | | `database.schema` | Tatsächliche Spalten, Typen, Nullbarkeit und Schlüssel lesen | | `database.rows` | Spalten auswählen, explizite Filter und begrenzte Seite lesen | | `database.count` | Vollständige Trefferzahl für dieselben Filter bestimmen | DB-Filter unterstützen `eq`, `ne`, `lt`, `le`, `gt`, `ge`, `contains`, `in`. Mehrere Filter werden mit AND verbunden. `contains` behandelt `%`, `_` und das Escape-Zeichen als literale Eingabe. Werte werden ausschließlich als SQL-Parameter übergeben. Tabellen und Spalten sind validierte Bezeichner; Tabellen müssen zusätzlich in der Betreiber-Allowlist liegen. Ein Schema-Lookup prüft die tatsächlichen Spalten. SQL-DECIMAL-Werte werden als exakte Zeichenfolgen übertragen, nicht gerundet. Textsuche ist lexikalisch: ein Treffer beweist weder eine semantische Definition noch eine Registrierung oder einen ausgeführten Aufruf. Auch historische SQL-Updates sind kein Ersatz für die aktuelle Datenbank. Pfad-Traversal, absolute Pfade und Git-Metadaten werden abgelehnt; Symlinks und Submodule werden nicht als lesbare Source-Dateien aufgenommen. Abfragen haben konfigurierbare Zeilen-, Byte-, Datei- und Zeitlimits. Ergebnisse markieren Pagination, ausgelassene Dateien und gekürzte Zeilen. `complete_filtered_rows` gilt nur für das genau beschriebene Prädikat ohne Offset/weitere Seite. Eine vollständige Abfrage mit null Treffern ist ein belastbarer Abwesenheitsbeleg **innerhalb dieses Snapshots und Prädikats**. Eine partielle Suche rechtfertigt keinen globalen Abwesenheitsschluss. ## Persistenz und Budget Captures besitzen Formatversion 1, Schema, Anfrage, Ursprung und Ergebnis. Git-Ursprünge enthalten URL und Commit; Treffer zusätzlich Blob-ID, SHA-256 und Zeilennummer. DB-Ursprünge enthalten Datenbank und die vom Betreiber vergebene Snapshot-Identität. Diese Identität muss beim Wechsel/Neuimport eines Snapshots ebenfalls geändert werden; sie ist keine automatisch berechnete Prüfsumme eines laufenden Servers. Investigation-Ergebnisse werden unter `analysis/evidence/captures/.json` zusammen mit Requests, Rohantworten und Evidence Records append-only gespeichert. Auch leere Antworten behalten Anfrage und Abdeckung. Historienprüfung und Wiedereinstieg benötigen keinen laufenden Git-/DB-Provider. Die Fallakte enthält die tatsächlich gelesenen Ausschnitte und Zeilen, nicht eine vollständige Kopie von Repository und World-DB. Ein Tool schreibt selbst nicht in den Case. Der bestehende Controller reserviert das positive Aktionsbudget für `project.query`, führt die Anfrage aus und übernimmt validierte Captures atomar mit der Evidenz. Wiederholte identische Ergebnisse sind keine neue Evidenz. Der In-Memory-Cache friert eine Antwort während Query/Validierung/Persistenz ein; bei einem Neustart sind gespeicherte Evidenz und der bestätigte Plan autoritativ. Ausfälle der Recherche führen zu `BLOCKED / RESEARCH_UNAVAILABLE`. Ein bestätigter Plan bleibt wiederaufnehmbar, einschließlich seiner Reservation. Ungültige Modellantworten führen nach dem konfigurierten Limit zu `BLOCKED / INVALID_LLM_RESPONSES`; eine technische Ablehnung beendet keine Hypothese und verändert keine vorhandene Evidenzbewertung. Nach Behebung ist eine explizite Operator-Wiederaufnahme über `CheckpointStore.advance` in den im letzten Blockierungscheckpoint hinterlegten `resume_state` nötig. Der Retry-Zeitpunkt und die bestehende Feedback-Sperre bleiben verbindlich. ## Case-Aufbereitung `CaseBuilder(..., project_research=service)` sammelt vor Abschluss einer Revision deterministisch konfigurierte Queries und Entity-Lookups. Namen und Questvoraussetzungen werden mit Provenienz als technische Fakten sichtbar, bevor die erste LLM-Anfrage beginnt. Das verhindert, dass eine numerische ID allein als Name oder betroffener NPC ausgelegt wird. Abfragen und Resultate liegen unter `research/project/`; die Case-Inputs werden mit einem neuen versionierten Index erweitert. Derselbe Enrichment-Checkpoint wird nicht wiederholt. Eine abgeschlossene Revision wird nicht nachträglich mit neuen Rohinputs umgeschrieben. Die Beispielkonfiguration löst Creature- und Quest-IDs generisch auf. Sie enthält keine BUG-000020-spezifische Diagnose oder Wolpertinger-ID. Die LLM kann während der Untersuchung weitere Tabellen und Source-Ausschnitte gezielt abfragen. ### Kontextorientierte Erweiterung Die Quest-Aufbereitung übernimmt nun auch `LogDescription`, `QuestDescription` und `StartItem`. Damit bleiben Handlung, Zweck des mitgegebenen Gegenstands und die beschriebenen Voraussetzungen sichtbar. Questtexte beschreiben die im Template formulierte Mechanik; sie beweisen keine tatsächlich ausgeführte Serveraktion. `enrichment.texts` hebt solche Texte als eigene `project_description`-Fakten mit Quellspalte, Rollenlabel und Herkunft hervor. Im numerischen Query-Fakt bleibt ein Textverweis statt einer zweiten Kopie. Die vollständige unveränderte Antwort liegt weiterhin im Capture; die Interpretation sieht den Text einmal in lesbarem Kontext. `enrichment.relationships` verfolgt ausdrücklich konfigurierte Beziehungen aus zurückgegebenen DB-Spalten. `from_table`, `from_column` und `from_id_column` bestimmen den Ursprung. `table`, `column` und `columns` bestimmen die folgende DB-Abfrage. `relation` benennt den Zusammenhang; `entity_type` ergänzt eine referenzierte Entität. Die Herkunft jeder Beziehung zeigt mit Capture und JSON-Pointer auf den Originalwert. Ein referenziertes Item wird nicht als beobachtetes Laufzeitobjekt oder Ursache markiert. Die Vcore-Konfiguration verfolgt mitgegebene/geforderte Items, Queststarter, SmartAI-Datensätze und ausdrücklich gebundene Creature-/Item-/Spell-Scriptnamen. SmartAI-Abfragen berücksichtigen `source_type = 0`, damit numerisch gleiche IDs anderer Entitätstypen nicht vermischt werden. Scriptnamen werden als Symbol gesucht, nicht als beliebige Zahl über die gesamte Historie. Weitere Beziehungen bleiben konfigurierbar; das generische Python-Modul enthält keine WoW-IDs oder Quest-Sonderfälle. `when_column`/`when_value` begrenzen bedingte Beziehungen. Standardmäßig werden null, leere Zeichenfolgen und numerische Null nicht verfolgt; `ignore_values` kann dies für andere Profile anpassen. Identische Queries werden nur einmal gesammelt. Zyklen und Verzweigungen sind durch konfigurierbare `max_queries`, `max_depth` und `max_relationships` begrenzt. Ausgelassene Abfragen/Beziehungen werden als `project_context_coverage` sichtbar statt als vollständige Ermittlung ausgegeben. Leere Item-Script- oder Queststarter-Resultate sind nur Befunde innerhalb dieser Tabellen. Sie beweisen weder ein fehlendes Item noch, dass eine Quest nicht durch einen anderen Mechanismus vergeben werden kann. Client-/Hotfix-Itemdaten werden durch diesen Meilenstein nicht importiert oder erfunden. Eine komplette Item→Spell→Creature-Kette ist deshalb nicht in jedem Case automatisch auflösbar; fehlende Teile bleiben Recherchebedarf. Mit aktivierter Projekt-Recherche wird `investigation_project_v1` verwendet. Der volle Context bleibt unverändert archiviert. Der Prompt lässt Input-Inventar und doppelte vorige Resultate weg; fremde Hypothesen behalten Evidenz-IDs/Provenienz, aber keine Payloads. Die Auswahl ist deterministisch und gekennzeichnet. Bereits gespeicherte Legacy-Prompts bleiben mit ihrer alten Version rekonstruierbar. Dies reduziert Kontextmenge, garantiert aber keinen bestimmten Eurobetrag und ersetzt keine Messung des nächsten LLM-Tests. ## Lokale Testumgebung Die eingerichtete Instanz benutzt MariaDB 10.11.14 aus dem offiziellen portablen ZIP (SHA-256 `838b7d91b871a30c2e42e5ceff9c6859ddd061b57328fb7cb51154af9bd8ddf4`). Es wird kein Windows-Dienst installiert. Daten und Logs liegen ausschließlich unter `service-data/research/`, Git-Clone ebenfalls dort. Diese Dateien werden nicht committet. Importiert wurde ausschließlich `cata_world` (206 Tabellen) aus dem bereitgestellten Backup, SHA-256 `4964f277ef5cf7f7f98bf4378971dcb55180bac813d49bcda6d2f03ff6969662`. Account-, Charakter- und weitere Datenbankabschnitte wurden ausgelassen. Die Instanz bindet `127.0.0.1:33079`; `viano_reader` besitzt SELECT ohne GRANT OPTION. `read_only` ist aktiviert, Abfragen verwenden zusätzlich eine Read-only-Transaktion. Der administrative Restore ist ein separates Entwicklungswerkzeug, keine Runtime-Funktion des Analyseprogramms und kein Modelltool. Optionale Dependencies installieren: ```powershell .\.venv\Scripts\python.exe -m pip install -e ".[research,test]" ``` `config/project.local.toml` wurde aus der Beispielkonfiguration vorbereitet. Das Programm lädt relative Providerpfade relativ zu dieser Konfigurationsdatei. Ein zukünftiger Server nutzt eigene lokale Zugangsdaten und dieselbe Schnittstelle mit SELECT-only-Rechten. Die bereits initialisierte Instanz bei Bedarf starten/stoppen: ```powershell .\.venv\Scripts\python.exe scripts/dev_world_db.py start --binary-dir service-data/research/runtime/portable/mariadb-10.11.14-winx64/bin --directory service-data/research/world-db .\.venv\Scripts\python.exe scripts/dev_world_db.py stop --binary-dir service-data/research/runtime/portable/mariadb-10.11.14-winx64/bin --directory service-data/research/world-db ``` Ein neuer Rechner benötigt zunächst den portablen Server, eine initialisierte Datadir (`mariadb-install-db.exe --datadir= --port=33079 --silent`) und einen einmaligen `dev_world_db.py restore --dump ` gegen diese neue lokale Instanz. Das Werkzeug verweigert Administration, wenn der Port zu einer anderen Datadir gehört. Zugangsdaten bleiben unter `service-data/`; `reader.local.txt` enthält nur das Reader-Passwort. Eine strukturierte Anfrage kann ohne LLM und ohne Paketänderung geprüft werden: ```json {"kind":"database.rows","table":"creature_template","columns":["entry","name"],"filters":[{"column":"name","operator":"contains","value":"Wolpertinger"}],"offset":0,"limit":10} ``` Als `service-data/research/query.json` speichern und ausführen: ```powershell .\.venv\Scripts\python.exe -m viano_bug_analysis.project_research --config config/project.local.toml --query service-data/research/query.json ``` Ein späterer, ausdrücklich gestarteter LLM-Trial erhält zusätzlich `--project-config config/project.local.toml`. Für den Qualitäts-/Kostenvergleich einen frischen Testcase verwenden: alte Hypothesen und frühere terminale Budgetstände werden durch diesen Meilenstein nicht heimlich korrigiert oder zurückgesetzt. Offline-Suite und optionale Integrationstests: ```powershell .\.venv\Scripts\python.exe -m pytest -q $env:VBA_TEST_PROJECT_CONFIG = "config/project.local.toml" .\.venv\Scripts\python.exe -m pytest tests/test_project_database_integration.py -q ``` Die Integrationstests prüfen den bekannten Backupstand ausschließlich lesend. Der CaseBuilder-Smoke-Test aus den echten wowdiag/wowsrc-Paketen liegt separat unter `service-data/research/smoke`; die ursprüngliche gestoppte Fallakte bleibt erhalten. Gemini unterstützt Google Search Grounding, wenn die API-Anwendung es aktiviert: https://ai.google.dev/gemini-api/docs/google-search . Der aktuelle OpenAI-kompatible Adapter setzt keine solchen Tools. Ein Gitea Runner ist für diesen Stand nicht nötig; er könnte später Indizes aufbauen, während die Analyse weiterhin nur lesend auf diese zugreift.