# Validation — V2.5.1 Reconnect / Presence / Heartbeat ## Regression: regelmäßiger HTTP 409 nach ~90 Sekunden 1. Browser-Client mit einem aktiven Task verbinden und mindestens 3 Minuten ohne F5 laufen lassen. 2. Prüfen, dass Auto-Guesses über die frühere 90-Sekunden-Grenze hinweg weiterlaufen. 3. DevTools → Network → WS beobachten: der Server sendet echte WebSocket-Ping-Control-Frames; Browser-Pongs verlängern die Read-Deadline. 4. Netzwerk für einige Sekunden trennen und wieder aktivieren. Der Browser muss automatisch reconnecten und ohne F5 weitertippen. 5. Während eines Task-Wechsels bzw. schnellen Reconnects darf ein verspätetes `ReleasePresence` der alten Verbindung die neue Presence nicht entfernen. Das wird zusätzlich durch `TestPresenceReconnectOldReleaseCannotDeleteNewLease` geprüft. 6. Shell-Client ebenfalls mindestens 3 Minuten laufen lassen, Verbindung kurz unterbrechen und prüfen, dass `[ws] wieder verbunden` erscheint und Guessing fortgesetzt wird. Maschinenlesbare 409-Codes: ```text presence_required sequence_mismatch task_config_changed selection_conflict task_inactive ``` ## V2.5 — Task-Auswahl, Vererbung und Shell-Client ### Browser-Landing-Page 1. Server mit mindestens zwei aktiven Tasks starten (`ACTIVE_TASK_COUNT=2`). 2. `/` in einem neuen Browserprofil öffnen. 3. Prüfen, dass vor der 3D-Ansicht die Task-Landing-Page erscheint. 4. Einen Task wählen und prüfen, dass WebSocket/Auto-Guess starten. 5. **TASKS** wählen, einen anderen Task anklicken und prüfen, dass keine `identity already connected`-Meldung entsteht. 6. Browser neu laden: die serverseitig zuletzt gewählte Task-Karte muss als ausgewählt markiert sein. ### Task-spezifisches NFT-Prompt + Admin-Draft 1. `/admin` → **TASK** → Task auswählen. 2. Anzeigename, Beschreibung, NFT-Prompt-Anweisungen und Negative Prompt eintippen, **noch nicht speichern**. 3. Mindestens einen Auto-Refresh-Zyklus (>3 s) abwarten und danach F5 drücken. 4. Prüfen, dass die noch nicht gespeicherten Eingaben weiter im Formular stehen. 5. **TASK-KONFIG SPEICHERN** verwenden und anschließend neu laden; nun müssen die Werte vom Server kommen. 6. Task gewinnen oder über Admin `close` beenden. 7. Folge-Task öffnen und prüfen, dass `range_bits`, task-spezifische Intervalle, Anzeigename, Beschreibung und beide Prompt-Felder identisch geerbt wurden. ### Shell-Client interaktiv ```bash go run ./cmd/client -url http://127.0.0.1:8080 ``` Prüfen: ```text tasks status map 25 leaderboard 20 nfts 10 use 2 status ``` Der Shell-Client muss weiter automatisch Tipps senden. Nach `use` darf der Server die neue WebSocket-Verbindung nicht wegen der alten Presence ablehnen. ### Shell-Client unbeaufsichtigt ```bash go run ./cmd/client \ -url http://127.0.0.1:8080 \ -non-interactive \ -task 1 \ -max-nodes 250 ``` Mindestens zwei Tippintervalle laufen lassen und im Admin prüfen, dass Guess-/Score-Aktivität vorhanden ist. Dann `Ctrl+C`; die Presence muss freigegeben werden. ### Browser-/Shell-Identity-Portabilität Im Browser eine Identity exportieren. Danach **Browser-Tab schließen**, damit die Single-Connection-Lease frei ist: ```bash export NEURALHUNT_IDENTITY_PASSPHRASE='test-passphrase' go run ./cmd/client \ -import ./neuralhunt-identity-....json \ -identity ./tmp-shell-identity.json ``` Die Client-ID muss mit der Browser-ID übereinstimmen. Umgekehrt: ```bash export NEURALHUNT_IDENTITY_PASSPHRASE='test-passphrase' go run ./cmd/client \ -identity ./tmp-shell-identity.json \ -export ./tmp-browser-import.json ``` Diese Datei anschließend über **Import** in einem Browserprofil laden; die Client-ID muss identisch bleiben. ### Automatische Tests Zusätzlich zu den bisherigen Tests: ```bash go test ./cmd/client ``` enthält PBKDF2-Testvektor sowie Raw-/verschlüsselten Identity-Roundtrip. `internal/data/successor_test.go` prüft mit einem echten SQLite-Treiber die Task-Vererbung, Idempotenz des Nachfolgers und das atomare Migrieren der Client-Auswahl. In dieser Erstellungsumgebung erfolgreich ausgeführt: ```bash node --check internal/webui/dist/app.js gofmt -w cmd/client/*.go internal/data/store.go internal/server/server.go internal/artifact/worker.go # kompletter Source-Compile mit lokalen API-Stubs für die drei nicht ladbaren Module go test -run '^$' ./... # echte Shell-Client-Krypto-/Identity-Tests; nur der WebSocket-Import ist dabei gestubbt go test ./cmd/client ``` Zusätzlich wurde eine **V2.4-artige SQLite-Datenbank** mit Python/SQLite angelegt und die Upgrade-Reihenfolge aus `OpenSQLite()` reproduziert. Dabei wurden die fünf neuen Task-Spalten, `client_task_selection` und der erst nach `parent_task_id` angelegte `tasks_parent_unique_idx` erfolgreich erzeugt. Das ist relevant, weil der Index bei Bestandsdatenbanken nicht vor dem `ALTER TABLE` angelegt werden darf. Ergebnis dieser ausführbaren Prüfungen: erfolgreich. ## Statisch geprüft / implementiert - UI ist weiterhin vollständig unter `internal/webui/dist/` eingebettet; kein npm/Vite-Build nötig. - `/leaderboard` ist eine SPA-Route und besitzt einen öffentlichen Live-WebSocket. - Mobile Mode ist für Client, Leaderboard und Admin implementiert; Admin bietet mobile MAP/TASKS/CONTROL-Navigation. - Das Leaderboard lädt Gewinner-Artefakte ausschließlich über `/api/public/artifacts/{task}/preview`. - Die öffentlichen Leaderboard-Daten enthalten NFT-Anzahl und letzte Wasserzeichen-Preview, aber keine Original-Artifact-URI. - Der frühere öffentliche `/artifacts/*`-File-Server ist entfernt; Original und Manifest benötigen ein Admin-JWT. - Raster-Previews werden serverseitig mit einem eingebrannten Bitmap-Wasserzeichen neu als PNG codiert; SVG-Previews erhalten eine wiederholte Wasserzeichenebene. - TARGET FIELD bildet Score monoton und direkt auf den Orbitalradius ab; Kameratiefe kann die wahrgenommene Nähe nicht mehr umkehren. - Score-Ringe: 0 / 25 / 50 / 75 / 90 / 95 / 99+; Vorder- und Rückhälfte werden zur Tiefenwahrnehmung unterschiedlich gerendert. - Pseudozufällige Client-zu-Client-Synapsen sind vollständig entfernt. - SIGNALWEGE verbinden ausschließlich Top-Kandidaten/eigenen Client mit dem Task-Core; Score-Verbesserungen erzeugen nur entlang dieses realen Pfads Signalimpulse. - Client lädt bei `task_changed` Range Bits, Seed, Revision, Intervalle und Punkte neu. - Client-Autosubmit verwendet keinen statischen `setInterval` mehr, sondern die jeweils aktuelle Task-Konfiguration. - Task-Aktionen werden in `task_actions` persistiert, jede Sekunde geprüft und mit Status/Audit gespeichert. - Alte SQLite-Datenbanken erhalten die neuen `tasks`-Spalten per `ALTER TABLE`-Migration. - `set_range_bits` unterstützt `preserve` und `reroll`. - Task-spezifische Tippintervalle überschreiben globale Runtime-Defaults. - OpenAI-, ComfyUI- und A1111-Provider speichern nur lokal erzeugte Ergebnisbytes + Manifest; kein S3 notwendig. - OpenAI-Provider validiert für `gpt-image-2` die dokumentierten Dimensionsgrenzen, bevor ein Request gesendet wird. - Provider-Secrets/URLs werden nicht über die Runtime-Settings-API exponiert. - ComfyUI unterstützt Workflow-Platzhalter für Prompt/Negative Prompt/Seed/Größe/Steps/Modell. - A1111 unterstützt optional Basic Auth und Checkpoint-Override pro Request. ## In dieser Umgebung nicht vollständig ausführbar `go test ./...` und `go mod tidy` können hier nicht abgeschlossen werden, weil ausgehender DNS/Netzwerkzugriff auf `proxy.golang.org` gesperrt ist. Der Versuch scheitert beim Download dieser bereits im `go.mod` referenzierten Module: ```text modernc.org/sqlite github.com/go-chi/chi/v5 github.com/gorilla/websocket ``` Dadurch konnten `internal/data`, `internal/server`, `internal/ws` und das komplette Binary hier nicht gegen frisch heruntergeladene Dependencies gebaut werden. Die direkt von ihnen unabhängigen geänderten Go-Packages wurden kompiliert/getestet. Docker ist in der Erstellungsumgebung ebenfalls nicht verfügbar. ## Empfohlene Tests auf deinem Rechner ```bash go mod tidy go test ./... go run ./cmd/server ``` Dann: 1. `/` öffnen und prüfen, dass TARGET FIELD standardmäßig aktiv ist. Einen Score-90-, Score-95- und Score-99-Punkt vergleichen: 99 muss unabhängig von Orbit/Neigung sichtbar auf dem kleinsten Ring liegen. 2. `ORBIT` laufen lassen und die Ansicht ziehen: Die Reihenfolge der Ringradien darf sich durch Kamerabewegung nicht umkehren. 3. `SIGNALWEGE` einschalten: Es dürfen keine Client→Client-Kanten erscheinen; nur ausgewählte Client→Task-Pfade sind erlaubt. 4. Zwei unabhängige Browserprofile auf `127.0.0.1` verbinden. 5. `/leaderboard` in einem dritten Tab öffnen; Punktänderungen sollen ohne manuelles Reload erscheinen. 6. Im Admin-Bereich einen aktiven Task auswählen und `pause` sofort ausführen. Clients müssen `PAUSE` anzeigen und keine Tipps senden. 7. `resume` ausführen; Autosubmit muss mit dem aktuellen Client-Intervall weiterlaufen. 8. `set_range_bits` im Modus `preserve` erhöhen; Client muss neue Bitzahl ohne Reload verwenden. 9. `set_range_bits` im Modus `reroll` ausführen; Punkte/Scores müssen auf 0 zurückfallen und neuer Seed/Revision aktiv werden. 10. Eine Range-Bit-Änderung fünf Minuten in die Zukunft planen und anschließend abbrechen; Status `cancelled` prüfen. 11. Zwei geplante Aktionen hintereinander setzen, z. B. 24 Bit und später 32 Bit, und Ausführung/Audit prüfen. 12. Einen kleinen Zahlenraum verwenden, einen Task lösen und lokales SVG + Manifest prüfen. 13. `/leaderboard` öffnen: das Gewinner-Artefakt muss in Galerie und Ranking nur als Wasserzeichen-Vorschau erscheinen. 14. Direkter Aufruf der früheren `/artifacts/...`-URI muss 404 liefern; Original/Manifest aus dem Admin-Dashboard müssen weiterhin nach Authentifizierung öffnen. 15. Browser auf <850 px setzen oder MOBILE aktivieren: Client-Details müssen einklappbar sein; im Admin müssen MAP/TASKS/CONTROL einzeln erreichbar sein. ### OpenAI Provider ```env ARTIFACT_PROVIDER=openai ARTIFACT_MODEL=gpt-image-2 OPENAI_API_KEY=... ARTIFACT_WIDTH=1024 ARTIFACT_HEIGHT=1024 ARTIFACT_QUALITY=medium ``` Task lösen bzw. bei einem abgeschlossenen Task `NFT-Bild neu erzeugen` ausführen. Erwartet: ```text /data/artifacts/artifact_/image.png /data/artifacts/artifact_/manifest.json ``` ### ComfyUI Provider ComfyUI starten, API-Workflow exportieren und im Workflow mindestens `{{PROMPT}}` sowie optional die anderen Platzhalter verwenden. Danach: ```env ARTIFACT_PROVIDER=comfyui COMFYUI_URL=http://127.0.0.1:8188 COMFYUI_WORKFLOW_PATH=/path/workflow-api.json ARTIFACT_MODEL=checkpoint.safetensors ``` ### AUTOMATIC1111 Provider A1111 mit `--api` starten: ```env ARTIFACT_PROVIDER=a1111 A1111_URL=http://127.0.0.1:7860 ARTIFACT_MODEL=checkpoint.safetensors ``` Bei `--api-auth user:pass` zusätzlich `A1111_USER` und `A1111_PASSWORD` setzen. ## Regressionen aus früheren Fixes - Auth challenge/login toleriert optionale JWK-Metadaten (`alg`, `use`, `kid` etc.). - Mehrere Challenges derselben Identität überschreiben einander nicht. - Eine Browser-ID darf weiterhin nur eine aktive Client-WebSocket-Verbindung besitzen. - Leere Listen werden als `[]`, nicht als `null`, an die UI geliefert. - Client-Punkt wird direkt beim WebSocket-Connect erzeugt. ## Additional v2 checks - SQLite schema parsed successfully with Python `sqlite3`, including `task_actions` and the live task override columns. - The legacy `migrations/001_init.sql` mirror was synchronized with `internal/data/schema.sql` so manual schema inspection does not show an obsolete layout. - Responsive CSS keeps the Admin Task Actions / Artifact control plane visible on common laptop widths (851–1280px). ## V2.1 package-integrity regression The V2 archive was missing `internal/data/`, which caused an old `store.go` to survive when users extracted over a previous checkout. That produced compile errors such as missing `Task.GuessMinIntervalSec`, `Task.Paused`, `LiveLeaderboard`, and `TaskAction`. V2.1 explicitly includes: - `internal/data/store.go` - `internal/data/schema.sql` - the matching V2 `internal/server/server.go` Always test from a clean extraction directory. ## V2.2 zusätzliche Validierung - `node --check internal/webui/dist/app.js` erfolgreich. - `go test ./...` wurde zusätzlich gegen lokale Compile-Stubs für `chi`, `gorilla/websocket` und `modernc.org/sqlite` ausgeführt, um alle Neural-Hunt-Packages inklusive `internal/server/watermark_test.go` ohne Netzwerkzugriff zu kompilieren. - `watermark_test.go` prüft Traversal-Abwehr, SVG-Wasserzeichen und Raster-Wasserzeichen. - Die erweiterten Leaderboard-/Artifact-SQL-Abfragen wurden gegen das reale `schema.sql` mit Python `sqlite3` ausgeführt. ## V2.4 Scale smoke test ```bash node --check internal/webui/dist/app.js go test ./internal/runtime ./internal/ws ./internal/server ./cmd/loadtest # Server in Terminal 1 go run ./cmd/server # Start small, then increase. go run ./cmd/loadtest -url http://127.0.0.1:8080 -clients 250 -ramp 10s -duration 1m -max-nodes 100 go run ./cmd/loadtest -url http://127.0.0.1:8080 -clients 1000 -ramp 30s -duration 2m -max-nodes 100 ``` Im Admin unter `/admin` sollten dabei `Guess/s`, `Improve/s`, `SQLite W/s`, `WS Frames/s`, `WS MB/s`, `Drops/s`, `Goroutines` und `Heap MB` live aktualisiert werden. Bei einem stabilen Test sollten `Drops/s` nahe 0 bleiben. Ein niedriger `SQLite W/s` relativ zu `Guess/s` bestätigt, dass verlierende Tipps den DB-Hotpath nicht mehr belasten. ## V2.5.2 Admin form stability regression The periodic 3-second admin telemetry refresh must never rebuild `#settingfields`. Only explicit task/tab changes or completed admin actions may refresh the control plane. Manual regression: 1. Open `/admin` and select `ARTIFACT`. 2. Open the Provider or Quality native select and leave it open for more than 6 seconds. 3. The dropdown must remain open while Overview/Performance counters continue updating. 4. Type into Prompt-Zusatz for more than 6 seconds; caret position and textarea scroll position must not jump. 5. Switch tabs and back; the draft value must still be restored. Static/syntax checks used for this release: ```bash node --check internal/webui/dist/app.js go test ./internal/webui ./internal/core ./internal/auth ./internal/artifact ./internal/settings ``` ## RIFT Medium + OpenAI usage/cost telemetry - Start with `ARTIFACT_PRESET=raccoon_full_art_v1` and no explicit `ARTIFACT_QUALITY`: the effective quality must be `medium`. - Generate one RIFT winner artifact and verify that the OpenAI request sends `quality=medium`. - Verify that `artifact_api_usage` receives one `artifact` row per successful winner-image API call and a `character_anchor` row when the canonical anchor is created. A successful call is logged even if the provider omits its optional `usage` block; in that case token counts remain zero and `estimated_cost_usd` is NULL. - When OpenAI returns usage data, verify that `input_tokens`, text/image input split, `output_tokens`, `total_tokens`, `estimated_cost_usd`, and `pricing_basis` are persisted and mirrored in the artifact manifest/provider metadata. - In Admin → Artifact, verify **KOSTEN HEUTE**, **Ø KOSTEN PRO KARTE**, **KOSTEN PRO 1.000 KARTEN**, and the recent-call table. Anchor cost belongs to today's total but must not affect the per-card average. - Verify `GET /api/admin/artifact/usage?day_start_ms=` returns the same aggregates. ## RIFT V2.8 — per-task style references + manual anchor - In Admin → ARTIFACT, with `OPENAI_API_KEY` configured and no existing `_collection/character_anchor.png`, click **RIFT-ANCHOR JETZT ERZEUGEN**. Verify exactly one OpenAI image-generation request is made, the file is stored at `data/artifacts/_collection/character_anchor.png`, the usage row has `kind=character_anchor`, and the UI changes to `LOCKED` without an overwrite button. - Verify a second direct `POST /api/admin/artifact/character-anchor` returns HTTP 409 while the anchor exists. - In Admin → TASK ACTIONS upload a JPEG/PNG style reference. Verify it is stored content-addressed under `data/artifacts/_styles/`, `tasks.nft_style_reference` receives only the basename, and the admin preview shows the uploaded image. - Verify the public Task landing card displays `/api/public/tasks//style-reference` and marks a custom task as `TASK STYLE`; tasks without an upload display the bundled default reference. - Complete a task and verify its successor inherits `nft_style_reference`. - Generate a RIFT winner card and inspect the multipart OpenAI edit request: `image[]` must contain two files in this order: `character_anchor.png`, then the task style reference. Provider metadata should contain `reference_mode=character-plus-task-style`, the character-anchor hash and the style-reference hash. - Use **AUF DEFAULT ZURÜCK** and verify the task DB reference is empty and generation falls back to `internal/artifact/assets/style_reference.jpg` without deleting shared content-addressed style files. ## V3.1 — Admin cleanup for stale non-winner profiles - In Admin → RUNTIME set e.g. `30 Tage` and click **PRÜFEN**. Verify the preview reports only clients whose `clients.last_seen` is older than the cutoff, that are not currently connected, and that have never appeared as `tasks.winner_client_id`. - Keep an old client connected via WebSocket: it must be reported as **aktuell verbunden geschützt** and never be deleted. - Create an old winner identity: it must be reported as **Gewinner geschützt** and never be deleted, regardless of age. - Confirm deletion and verify the client row is removed together with cascading `task_points`, `client_unlocks`, and `client_task_selection` rows. - Verify a recent non-winner remains untouched. - Verify WebSocket connect and disconnect update `clients.last_seen`, so a long-running session starts its inactivity window at disconnect rather than at its original login. - The API rejects cleanup windows shorter than one hour. Relevant automated tests: `internal/data/profile_cleanup_test.go` and `internal/server/profile_cleanup_test.go`.