# HANDOFF 2026-09-02 — infografiki na planszach, warstwy, lab wycinek

> Stan na koniec sesji 2026-09-01/02 (pauza na kwotę 5 h). Historia decyzji z poprzednich
> etapów: `HANDOFF-2026-08-31.md` (§1–§14 = fabryka do V3; §15–§30 = dziennik tej sesji,
> chronologicznie, z dowodami). Ten plik = stan i wznowienie, bez powtarzania historii.

## 0. Jednym spojrzeniem (stan po rundzie 2, 2026-09-02 popołudnie)

- **Nic nie biegnie.** Lab wycinek zamknięty: A2 i C z raportami, B2 zatrzymany przez usera
  (wyniki iteracji 3 na dysku, bez raportu). Ostatni bieg `20260902-mendel-ig6` zakończony.
- **Działa end-to-end i jest zmierzone — łańcuch „0 LLM po stronie obrazu"**: agent wybiera
  PRESET z rejestru (tabela decyzyjna w bloku v5) → `prompt_szablon.py` składa prompt rysownika
  bez LLM → gpt-image-2 → sloty presetu = regiony (bez wizji) → maski pikselowe całych obiektów →
  sędzia bez LLM → bramka (ODRZUCONY/1 region = obraz w całości). ig6: 7/7 plansz, 1,75M tokenów,
  14 min (najszybszy bieg), sędzia 7× UWAGI, 0 ciężkich; obrazy 75–144 s, 0 wywołań wizji/codexa.
- Wcześniejsze filary bez zmian: infografika jako nośnik treści (ig3), maski zamiast prostokątów
  (ig5), lektor m04 przez box 5060 Ti, fonty z polskimi glifami, mozaika harnessa naprawiona.
- **Iteracja 7 (§6d)**: szkielet bez tuszu — pył, sporne klamry i blade wypełnienia idą do warstw właścicieli;
  sędzia M7a (brud w klatce 0) = 0 px w 7/7 na tych samych obrazach; ig7 wyrenderowany, film i strony gotowe → werdykt usera.
- **Iteracja 8 (§6e)**: kontrola nagości wizją (0 $) = TYLKO FLAGA (user: zakaz w promptach zakryłby wszystko) + łagodne
  wejścia warstw (`fadeIn` 1 s, nigdy `pop`). ig8 = ig7 z t05 na nowo (ubrane) i bez `pop`.
- **Kickery (§6f)**: reguła „w języku lektora” w REZYSER_V3.md; PMENDb/ig8 przetłumaczone na polski i przerenderowane.
- **Walidacja promptów codexem (§6g)**: 30 znalezisk, 21 poprawek (REŻYSER, T1/T2, blok v5, reguły, schematy, regiony).
- **Stanowisko usera (2026-09-02 wieczór, po porównaniu z wytwórnią):** nie wybierać jednej metody teraz; różne treści mogą
  dostać różne potoki (przewidywalne → à la wytwórnia; schematowe → projektowanie opusowe; rozdziały książek → obraz +
  odkrywki + HyperFrames). Wszystko jest w fazie testów skalowania. **Cel: tysiące, dziesiątki tysięcy filmów.**
  Dokumenty: `POROWNANIE-WYTWORNIA-vs-FABRYKA-2026-09-02.md`, canvas `www/wytwornia-czy-fabryka.html`.
- **Qwen (GPU server `:8020`) — stanowisko usera:** „darmowy”, ale przy tysiącach filmów będzie wąskim gardłem; używać do
  krótkich sprawdzeń i rzeczy o małej pracy per film (dziś: kontrola nagości ~2 s/obraz, wizja regionów tylko w presecie
  `wolny`), nigdy w pętli składania planszy; przy skali — próbkowanie, nie pełne przeglądy.
- **Do zrobienia** (§6): kontrola diakrytyków OCR-em w sędzim, decyzja o strzałkach między
  slotami, kicker w języku lektora (REŻYSER), dokończenie B (raport, reguły M4 — etykiety to
  jedyne pozostałe odrzucenia), ≥3 obrazy na rodzinę presetów, decyzja usera: czy tryb `warstwy`
  (v3) zostaje standardem produkcyjnym.

## 0a. Wcześniej w tej sesji (przed infografikami) — skrót z dowodami w starym HANDOFF

- **Audyt kosztów Opusa z transkryptów** (`~/.claude/projects/…`, dedup po `message.id`): 76 %
  „świeżych" to zapis cache w pętli narzędziowej; sieroty po killach 33,4 M/29 % dnia → doktryna
  STOP-flaga (drain), nigdy pkill; INCOMPLETE_LIMIT nie stawia `.gotowe` (naprawa, odzysk 226 scen).
  Kanon: `external_connections/claude_code_sdk/03-claude-p-cli.md`; memory
  `audyt-petli-agenta-przy-masowce` (jedyny wpis memory z tej pracy). (stary §8–§9)
- **Standard produkcyjny** ustalony w review: Opus 5 (`claude-opus-5[1m]`) · 2 tury · wiedza pełna;
  slim i metryka Similarity odrzucone; T2 przez `--resume` ogląda mozaikę → KEEP/ACCEPT/ROLLBACK.
- **Biblioteka elementów**: 82 typy / 285 egzemplarzy z 264 scen (codexy: inwentarz → wizja →
  synteza → wycinki), nazwy wg FORMY, indeks NA ŻĄDANIE; próba ognia (agent odrzucił `compare`
  cytując anty-użycie). (stary §10–§11)
- **Workflow V3** (tekst → REŻYSER_V3 tablice z DOMINANTĄ + wsparciem → TTS per tablica →
  masówka): iteracje na MHsO — bez zalewu, gospodarz kadru, strefa ciszy 3 s jako twardy błąd
  walidatora, dur = voice bez holdu. (stary §12)
- **Skiny**: uniwersalność poza dark mode, doktryna „tylko farba" w `nadpisy_css` + lint
  `08_skiny.py`, konwencja SKINY.md; bug `#01-p1-bg` (id od cyfry) — tło nigdy nie malowane.
  (stary §13)
- **Arsenał HyperFrames**: 357 pozycji zainstalowanych w `bloki/` (139 bloków + 218 składników),
  157 podglądów mp4, strona `www/bloki.html` naprawiona na play (proporcje pionowych bloków,
  f-string `{{}}` pułapka), serwer www jako LaunchAgent `pl.gileneo.fabryka-www` (KeepAlive).
- **Drzewo prezentacji dla modela** (§16–§17 starego): poziom 0/1/2 dla biblioteki i arsenału,
  test dymny (20 kB zamiast 84 kB, 6 luk protokołu wszytych), kategorie poziomu 0 z codexa.
- Kanał co_kraj_to_obyczaj: job 969 na mini (detektor plansz `10_hyperframes.py`) — zamrożony.

## 1. Co powstało (narzędzia, wg katalogów)

| plik | rola | stan |
|---|---|---|
| `baza/INDEKS.md`, `bloki/INDEKS.md`, `bloki/kategorie/*.md` | prezentacja biblioteki i arsenału dla agenta DRZEWEM (poziom 0 ≈ 12+3 kB, sekcje tematyczne z `baza/_kategorie.json`; poziom 1 karta/kategoria; poziom 2 kod) | gotowe, test dymny: 20 kB zamiast 84 kB |
| `fabryka/pilot/infografika.mjs` | agent zamawia obraz: `--tryb pojeciowy\|bogaty`, `--styl papier\|skin`, `--aspekt`, `--etykiety`, `--nazwy "etykieta \| opis"`, `--naglowek`, `--schemat <preset>`, `--prompt szablon` (bez codexa), `--ramki brak\|obiekt`, `--regiony --prefix pl`, `--warstwy maski\|wycinanie\|reflektor` — krok A codex2 lub szablon, krok B imagegen (+ spłaszczanie PNG z alfą na tło stylu), krok D mapa regionów + snippet + **bramka sędziego** (`SĘDZIA: …`, ODRZUCONY/1 region → tylko snippet całościowy); **walidacja = flagi, nigdy odrzucenie**; limity 2 obrazy/3 próby/scenę (wpisy sędziego nie liczą się jako próby); semafor 4 sloty, 600 s | produkcyjne (ig6) |
| `fabryka/pilot/INFOGRAFIKA_REGULY.md`, `…_BOGATE.md` | reguły promptu rysownika (pojęciowy / bogaty + §4a schematy; nagłówek zwykłą pisownią — model gubi ogonki w wersalikach) | gotowe |
| `fabryka/pilot/schematy.py` | REJESTR 12 presetów (rzad, proces, pion, stos, os-czasu, os-czasu-pion, mapa-mysli, centrum-boki, porownanie, siatka, cykl, **wolny**): sloty partycjonujące płótno (geometria v5: etykieta w slocie obiektu, rynny 3–6 %), `KOLEJNOSC`, `TABELA_DECYZYJNA`, `PRESETY` (zdania szkieletu), `sprawdz()`, `--test` (66 wariantów), `--rejestr`, `nazwy_regionow()`; `opis_layoutu(…, ramki)` do promptu | gotowy (A2); bezpieczne: proces/rzad, cykl, porownanie, centrum-boki, wolny; warunkowy: mapa-mysli; niezmierzone: os-czasu, stos, pion, siatka |
| `fabryka/pilot/regiony.py` | mapa regionów: sloty presetu → bez LLM (nagłówek z jawną rolą, bug nazw naprawiony); inaczej wizja qwen-flash `:8020` (0 $) / codex2 `-i` / piksele (`--model piksele`, iter3 B: 0 cięć, 0 rozdarć); snap do rynien, przydział składowych tuszu (obiekt w całości, strzępy, linia bazowa napisu, etykieta do obiektu, ramka do regionu), **maski PNG „LA"** per region + reszta, snippet CSS/HTML/OPS; **domknięcie (iter. 7)**: pył, składowe sporne i nieprzydzielone → warstwa właściciela najbliższego tuszu (sporne → późniejszy region), blade piksele (16 < odch. ≤ 60) ≤ 5 % W od tuszu → właściciel, dalsze → spłaszczone (podkład), reszta = sam papier | 1830 linii; zweryfikowany po zatrzymaniu B2 (wizja z logu OK, schemat OK); kopia sprzed iter3: `_lab-wycinki/B/regiony.przed_iter3.py` |
| `fabryka/pilot/prompt_szablon.py` | prompt rysownika BEZ LLM z presetu + `--nazwy "etykieta \| opis"` + `--naglowek`; v5: etykieta w slocie obiektu, tło kryjące, zakaz ramek, dosłowna pisownia z diakrytykami przy każdym napisie | produkcyjny (ig6); M1 4,9 % vs codex 11,4 % |
| `fabryka/pilot/kontrola_tresci.py` | kontrola treści obrazu wizją qwen-flash (0 $, ~2 s): liczba postaci + NAGOŚĆ → tylko FLAGA (decyzja usera); wpięta w `infografika.mjs` po kroku B | gotowy (1 TP, 6 TN na ig7) |
| `fabryka/pilot/lab_wycinki_metryki.py` | SĘDZIA bez człowieka (C): symulacja odsłaniania klatkami, M1–M6 (+M6c puste ramki), werdykt CZYSTY/UWAGI/ODRZUCONY z zarzutami i lokalizacją, `NN.metryki.json`/`werdykt.txt`/`symulacja.png`; wpięty jako bramka w `infografika.mjs`; **M7a** brud w klatce 0 (nie-papier > 16, z pyłem; ≥ 200 px ciężki) i **M7b** spłaszczone na zawsze (> 2 % tuszu ciężki) | gotowy, skalibrowany (prostokąty ig4 → ODRZUCONE, maski → UWAGI) |
| `fabryka/wzorcownia/06_masowka.py` | `--infografiki dowolne\|zawsze\|warstwy`; bloki promptu v2/v3/v4/**v5** (codex1 z briefów; v5 = tabela presetów, szablon, dwie ścieżki wg `SĘDZIA:`) + „wywołanie w pierwszym planie, nigdy w tle"; tokeny «INFOGRAFIKA*» w PROMPT_T1/T2 | produkcyjne |
| `fabryka/biblioteka/10_polski_glos.py` | lektor PL: omnivoice m04 + loudnorm, box CT111 wprost (`--backend box`), words z faster-whisper (venv w scratchpadzie), rotacja audio po sukcesie | produkcyjne |
| `…/11_porownanie.py`, `12_propaguj_audio.py`, `13_przeglad.py` | strona porównawcza filmów, wgranie nowego audio do istniejących scen + re-render, strona-przegląd mozaik i infografik | gotowe |
| `fabryka/fonty/*.woff2` | latin + latin-ext (były subsety bez polskich znaków!); Gochi Hand bez PL (skiny blackboard/repochad-hand nie na polski) | wymienione; sceny mają WŁASNE kopie |
| `fabryka/pilot/harness.mjs` | naprawa mozaiki 2×3 (`xstack` `0_h0+h2`) — 1314 mozaik przebudowanych | naprawione |

## 2. Biegi tej sesji (Mendel = `produkcje/PMENDb`, 7 tablic)

Źródło Mendla: `misc/podreczniki/kurs/lekcje/biology-2e/v1/jednostki/m66486__00.json`
(feynmanowe omówienie modułu *Mendel's Experiments…* z Biology 2e; pole `tekst`, 13 akapitów,
1486 słów, `sekundy` 355) → `fabryka/e2/scenariusz_MENDbio_czasy.txt` (akapit = scena, czasy
proporcjonalne) → `06_produkcja.py --vid MENDbio --pid PMENDb --binarka codex1` → REŻYSER 7
tablic → szkielety (Kokoro) → `10_polski_glos.py` (m01 → potem m04). Enzymy: wytwórnia
`misc/wytwornia/projekty/biologia_2011_rozszerzony_1__z7-w1/scenariusz.json` →
`scenariusz_WYTWbio_czasy.txt` → `PWYTB`. Falstart: `scenariusz_ADHDbio_czasy.txt` +
`produkcje/PADHDb/` (bieg przerwany na REŻYSERZE, nie używać; `produkcje/KATALOG.jsonl` = 60 scen
łącznie z tymi śladami). Listy dogrywek: `przebiegi/_tidy_ig3b|ig3c|ig5b.txt`.

| bieg | tryb | obrazów w kodzie | świeże tokeny | wynik / uwaga |
|---|---|---|---|---|
| `20260901-mendel-base` | bez narzędzia | — | 2,10M | 7× ACCEPT_T2 |
| `20260901-mendel-ig` | blok „opcjonalny" | 0/7 | 1,54M | narzędzie niewidzialne dla agenta |
| `20260901-mendel-ig2` | decyzja obowiązkowa | 1/7 | 1,50M | 6× NIE z rzeczowym uzasadnieniem |
| `20260901-mendel-ig3`+b+c | infografika-first (bogaty) | 7/7 | 2,72M | dogrywki przez limity NARZĘDZIA (semafor, walidator) |
| `20260902-mendel-ig4` | warstwy v1 (prostokąty) | 7/7 | 1,43M | user: „dziury creepy" |
| `20260902-mendel-ig5`+b | warstwy v2 (maski) | 7/7 | 2,06M | t07 PUSTY_PRODUKT (tło) → dogrywka OK |
| `20260902-mendel-ig6` | warstwy v3: presety + szablon (0 LLM) + maski ze slotów + sędzia | 7/7 | 1,75M | **14 min**; sędzia 7× UWAGI, 0 ciężkich; presety: proces ×4, mapa-mysli, porownanie, centrum-boki |
| `20260902-mendel-ig7` | ig6 + **domknięcie masek** (szkielet bez tuszu: pył/sporne/blade → właściciele), te same 7 obrazów, bez LLM | 7/7 | 0 (kopia ig6) | sędzia M7a brud w klatce 0: **0 px w 7/7** (ig6: 533–31 498 px); regiony identyczne z ig6 |
| `20260902-mendel-ig8` | ig7 + kontrola nagości (flaga qwen; t05 na nowo) + łagodne wejścia (`fadeIn` 1 s zamiast `pop`) | 7/7 | 0 (kopia ig7) | t05: 8 postaci UBRANYCH za 1. razem (76 s, 0,01 $); render 7/7, strony 8 kolumn |
| `20260901-wytwbio-test` | enzymy (wytwórnia), bogaty | — | 0,97M | 5/5 ACCEPT_T2; porównanie z filmem wytwórni |

Strony (serwer `:8899`): `www/produkcja_PMENDb.html` (6 kolumn: baseline / ig2 / ig3 / ig4 / ig5 / ig6),
`www/produkcja_PWYTB.html`, `www/przeglad_ig.html` (78 kafli: mozaiki + wszystkie infografiki),
`www/lab_wycinki_{A,C}.html` (galerie labu; B bez galerii).
Filmy: `przebiegi/<bieg>/FILM.mp4` (ig3, ig5 sklejane z dogrywkami wg `FILM.LEDGER.json`).
Wszystkie sceny przerenderowane z lektorem m04 i fontami PL (37 scen, 0 błędów).

## 3. Werdykty usera (obowiązują)

1. **Walidacja = flagi, nigdy odrzucenie** obrazu (doktryna wytwórni).
2. Nieodsłonięte ma **nie istnieć** na ekranie: żadnych przyciemnionych podglądów (reflektor
   odrzucony) ani pustych ramek-placeholderów.
3. Obraz na planszy jest wartościowy („uczłowiecza"), ale animowanie go jest trudne → dwie
   opcje: cały naraz albo wycinki; wycinki wymagają **systemu bez human review**, zmierzonego
   na kilkunastu obrazach, najtańszego w tokenach (najlepiej 0 LLM przy cięciu).
4. Preset kompozycji dowolny (mapa myśli była przykładem) — warunek: rysownik i wycinacz
   dzielą tę samą definicję („byleby to grało ze sobą"); rodowód: warianty pól w image_generator.
5. Lektor PL: **m04** („Charon/Informative") + loudnorm, box 5060 Ti; m01 = „upośledzony".
6. Standard produkcyjny bez zmian: Opus 5 · 2 tury · pełna; prompty produkcyjne pisze codex1
   z briefów; zero retry; STOP-flaga, nie pkill.

## 4. Pułapki odkryte w tej sesji (każda z dowodem w starym HANDOFF)

- masówka nie dopuszcza `python3` w allowedTools → narzędzia agentów tylko w node (`Bash(node *)`).
- `codex2 …` (codex-engine) przy limicie konta czeka do 6 h → `AI_WAIT_MAX_S=0`.
- Frame'y HyperFrames są inline'owane; runtime przepisuje tylko `../` → gołe `assets/…` OK.
- Sceny mają WŁASNE kopie fontów i audio (copytree szkieletu) — zmiana w repo nie dosięga
  istniejących scen; `12_propaguj_audio.py` / ręczne cp + re-render.
- `xstack` nie liczy `2*h0` → mozaiki miały f5/f6 na wierzchu f1/f2 (naprawione).
- CSS `mask-image` czyta ALFĘ: PNG „L" = brak maskowania; maski zapisywać jako „LA".
- `regiony.py --surowe` musi przenosić surową odpowiedź modelu do nowego logu.
- Moduł TTS `:8772` idzie tunelem (503 gdy tunel nie wstał); Mac widzi CT111 po LAN → box wprost.
  CT111 ma JEDNĄ kartę (5060 Ti = gpu0); `tts-omni-gpu1` to zombie bez GPU — trzymać `inactive`.
- Agent uruchamiający narzędzie w tle w `-p` kończy turę bez planszy → zdanie o pierwszym planie.
- Strażnik etykiet musi porównywać bez diakrytyków („wyglada" = „wygląda").
- 7 agentów zamawiających obrazy naraz zjada 2 sloty × 180 s → 4 sloty, 600 s.
- REŻYSER_V3 daje angielskie kickery przy polskim lektorze (brief dla codex1 — otwarte).
- gpt-image-2 gubi ogonki w WERSALIKACH („CIAGLA") — nagłówki zwykłą pisownią.

## 5. Lab wycinek — stan i wnioski (szczegóły: `przebiegi/_lab-wycinki/`, C/RAPORT.md, §29 starego HANDOFF)

- **C (sędzia, zakończony)**: prostokąty ig4 7/7 ODRZUCONE (śr. 4,1 ciężkich, 14 % tuszu
  w szkielecie); maski 0,0 cięć, 1,2 % w szkielecie, 7/21 odrzuconych — WSZYSTKIE przez etykiety
  (M4: podpis rozcięty między warstwy / region-etykieta przed obiektem). Progi w pustej strefie
  (cięcia prostokątów mediana 2833 px vs masek 62 px). Rekomendacja: bramka po `regiony.py`.
- **B (pikselowo; iter3 zrobiona, B2 zatrzymany przez usera bez raportu)**: wariant bez wizji
  ≈ wizja (iter3: M3a 0 u obu, M6 0, M4 0,5–0,9 zarzutu/obraz); słabe tylko NAZWY regionów bez
  schematu → ze schematem nazwy z góry = 0 LLM. Zrobione w `regiony.py`: bug nazw `--schemat`,
  linia bazowa napisu w jednym regionie, etykieta z obiektem, ramka do regionu. Etykiety (M4) =
  jedyne pozostałe odrzucenia sędziego. Wyniki: `B/wyniki_iter1..3`.
- **A2 (schematy + szablon, zakończony, RAPORT.md)**: 16 obrazów; geometria v5 — 0/9 odrzuceń
  sędziego, szablon bez LLM M1 4,9 % vs codex 11,4 %; presety bezpieczne: proces/rzad, cykl,
  porownanie, centrum-boki, wolny; mapa-mysli warunkowa (0 obrazów w v5); os-czasu/stos/pion/
  siatka niezmierzone. Poprawki v5: etykieta w slocie obiektu, tło kryjące, zakaz ramek.
- **Docelowy łańcuch (tani) — ZREALIZOWANY w ig6 (7/7, 14 min, 0 LLM po stronie obrazu)**: schemat z rejestru → prompt-szablon (0 LLM) → maski ze slotów
  (0 LLM) → sędzia (0 LLM) → bramka: CZYSTY/UWAGI = warstwy, ODRZUCONY = obraz w całości
  jednym opem + flaga. Wizja tylko dla presetu `wolny`.

## 6. Otwarte wątki (stan po ig6)

1. **Diakrytyki na obrazach**: rysownik gubi ogonki nawet w zwykłej pisowni (ig6 t05 „ciagla",
   „Bialy"); szablon już wymusza dosłowną pisownię przy każdym napisie — do zmierzenia; w sędzim
   kontrola OCR napisów vs lista etykiet jako flaga lekka (nie odrzucenie).
2. **Strzałki między slotami** (proces) trafiają do następnego obiektu zamiast do szkieletu —
   akceptowalne, do decyzji usera (widz widzi strzałkę razem z kolejnym krokiem).
3. **B (cięcie pikselowe)**: raport nie powstał; reguły M4 z iteracji 3 są w `regiony.py`
   (etykiety = jedyne pozostałe odrzucenia sędziego, ~0,5–0,9 zarzutu/obraz); ostatni debug:
   etykieta „wzrost" w nagłówku (lab A obraz 08). Ewentualnie nowy agent B3 z briefem §5.
4. **Presety**: os-czasu, stos, pion, siatka niezmierzone obrazem; mapa-mysli warunkowa
   (0 obrazów w geometrii v5); docelowo ≥3 obrazy na rodzinę.
5. ~~REŻYSER_V3: kicker w języku lektora~~ — ZROBIONE (§6f): reguła w REZYSER_V3.md, PMENDb przetłumaczone; PWYTB nadal po angielsku.
6. **Decyzja usera**: czy tryb `--infografiki warstwy` (v3) zostaje standardem produkcyjnym
   obok „Opus 5 · 2 tury · pełna"; czy `wolny` (wizja) ma być dozwolony w masówce (koszt 0 $, qwen).
7. Zamrożone od wcześniej: masówka kanału 91/355, FJKiX-t02, poczekalnia 44 typów, kanał
   co_kraj_to_obyczaj, 9 bloków code-snippet bez podglądu.
8. **Utrwalić venv whispera** (`10_polski_glos.py --venv` wskazuje ulotny scratchpad):
   `python3 -m venv fabryka/.venv_asr && fabryka/.venv_asr/bin/pip install faster-whisper`.
9. Pinować `HF_BIN` w masówce (runtime 0.8.23 z cache npx); sprzątanie śladów falstartu
   (`produkcje/PADHDb`, `scenariusz_ADHDbio`) — decyzja usera.

## 6a. Wznowienie labu 2026-09-02 (po odnowieniu kwoty)

Zatrzymani przez `TaskStop` agenci NIE dają się wznowić (transkrypt przepada) — uruchomione
NOWE agenty A2 i B2 z briefem wspólnym + stanem katalogów + priorytetami z raportu C (raporty
docelowe: `_lab-wycinki/A/RAPORT.md`, `_lab-wycinki/B/RAPORT.md`). Lekcja: pauza agenta =
wiadomość „zapisz stan i zakończ", nie TaskStop; agentom zawsze: „narzędzia w pierwszym planie,
nie czekaj na powiadomienia z tła".

## 6b. Raport A2 (rejestr presetów + prompt-szablon) — 2026-09-02

16 obrazów (~$0,08, 71–154 s), wszystkie ocenione sędzią C (`C/tabela.md` 159 wierszy).
- Geometria v5 (etykieta W SLOCIE własnego obiektu, rysunek wolnostojący, tło kryjące):
  it1–2 3/7 ODRZUCONE (wszystkie M4: etykieta w pasie nagłówka/cudzym slocie/rynnie),
  it3–4 **0/9 ODRZUCONYCH**, M3a 0/16, M6 0/16.
- Szablon (0 LLM) vs codex na tym samym presecie: M1 średnio **4,9 % vs 11,4 %** tuszu poza
  slotami, czas 71–101 s vs 106–154 s (codex dokłada ozdoby poza slotami i tło z alfą —
  `infografika.mjs` spłaszcza teraz PNG z alfą na tło stylu, oryginał `NN.rgba.png`, flaga).
- Ramki: `--ramki obiekt` → M6c 0 (karta wchodzi z treścią), koszt M1 +3,8 pp; DOMYŚLNIE `brak`.
- `wolny` (części rozłączne, etykieta pod częścią, tło kryjące): 2/2 UWAGI, 4–5 regionów,
  1 wywołanie qwen (0 $); szablon = codex.
- Bezpieczne presety: proces/rzad, cykl, porownanie, centrum-boki, wolny. Warunkowy:
  mapa-mysli (0 obrazów w v5). Niezmierzone: os-czasu, stos, pion, siatka (geometria `--test`).
- `schematy.py`: rejestr 12 rodzin, `KOLEJNOSC`, `TABELA_DECYZYJNA`, `sprawdz()`, `--test`,
  `--rejestr`, `nazwy_regionow()`; `prompt_szablon.py` v5; raport `A/RAPORT.md` (6 zdań do
  bloku masówki), galeria `www/lab_wycinki_A.html`.
Do B: region „nagłówek" ze schematu bez jawnej roli bywał scalany jako etykieta (naprawiony w B2: jawna rola `naglowek`).

## 6c. Bramka sędziego wdrożona (2026-09-02)

`infografika.mjs --regiony`: po `REGIONY GOTOWE` uruchamia `lab_wycinki_metryki.py --tryb maski
--ocr off` (0 LLM, ~2 s), czyta `NN.metryki.json` i wypisuje linię `SĘDZIA: <werdykt> (…)`:
ODRZUCONY albo <2 regiony → snippet warstw OBCIĘTY, agent dostaje tylko snippet całościowy
(„WPNIJ DOKŁADNIE TAK"); CZYSTY/UWAGI → pełny blok CSS/HTML/OPS. Werdykt i liczby trafiają do
`infografiki.jsonl` (`sedzia`, `ciezkie`, `lekkie`, `regionow`, `calosc`). Awaria sędziego =
flaga, warstwy bez oceny. Blok promptu v5 (codex1 z briefu `ig5_brief.txt` w scratchpadzie):
wybór presetu z tabeli decyzyjnej, `--prompt szablon --naglowek --nazwy "etykieta | opis"`,
dwie ścieżki wpięcia wg `SĘDZIA:`.
Blok v5 (codex1) wklejony do `INFOGRAFIKA_BLOK_WARSTWY` + WEJŚCIE/KROK: tabela decyzyjna
presetów, `--prompt szablon --naglowek --nazwy "etykieta | opis"`, `--ramki brak`, pierwszy
plan, dwie ścieżki wpięcia wg `SĘDZIA:`. Test bramki: `przebiegi/_test-bramka/PMENDb-t04`.
Test bramki (`_test-bramka/PMENDb-t04`, proces:4, szablon): prompt 0 s, obraz 77 s, regiony ze
slotów 2,9 s bez wizji, 5 regionów = całe obiekty z etykietami, `SĘDZIA: UWAGI (0 ciężkich,
26 lekkich)` → warstwy. Strzałki między slotami przydzielone do następnego obiektu (akceptowalne).
Pełny łańcuch „0 LLM po stronie obrazu" potwierdzony end-to-end.
B2 zatrzymany przez usera przed raportem (debugował ostatnią etykietę „wzrost" w nagłówku).
Stan `regiony.py` (1756 linii) zweryfikowany przez koordynatora: składnia OK, ścieżka wizji
z logu OK (9 regionów), ścieżka `--schemat` OK z poprawnie nazwanym nagłówkiem (bug naprawiony).
Wyniki iteracji 3 w `_lab-wycinki/B/wyniki_iter3/` (36 pozycji, metryki.csv), brak RAPORT.md;
kopia sprzed iteracji 3: `B/regiony.przed_iter3.py`. Bieg `20260902-mendel-ig6` (claude1):
pełny łańcuch — blok v5, `--schemat` z tabeli decyzyjnej, `--prompt szablon`, maski ze slotów,
bramka sędziego.
Liczby B (średnie per obraz, sędzia własny B; a = qwen+składowe, b = pikselowo, c = sloty
schematu, bo = pikselowo+OCR): iter1 a M3a 0,68/M4 1,47/M6 1,26, b 1,21/1,05/2,89 → iter3
a 0,00/0,87/0,00, b 0,00/0,71/0,00, c 0,00/0,50/0,00 (n=31/31/8). Cięcia i rozdarcia zerowe
we wszystkich wariantach; zostają etykiety (M4 ≈ 0,5–0,9 na obraz), najniżej ze slotami schematu.

### Werdykt ig6 (`20260902-mendel-ig6`, pełny łańcuch bez LLM po stronie obrazu)
7/7 OK · **1,75M · 14 min** (najszybszy bieg — brak czekania na codex w kroku A) · ACCEPT_T2 5,
KEEP_T1 2. Obrazy 75–144 s, presety wybrane przez agentów z tabeli: proces:4 ×2, proces:3 ×2,
mapa-mysli:5, porownanie, centrum-boki; regiony ze slotów (3–7), wszystkie warstwy wpięte,
check CZYSTY 7/7; **sędzia: 7× UWAGI, 0 ciężkich** (lekkie 3–71) → wszędzie ścieżka warstw.
Film `przebiegi/20260902-mendel-ig6/FILM.mp4`; strona `produkcja_PMENDb.html` 6 kolumn;
`przeglad_ig.html`. Koszt obrazów 7 × $0,01, 0 wywołań wizji, 0 codex w kroku A.
Skaza ig6 (t05, prompt-szablon): rysownik zgubił polskie ogonki w nagłówku i etykietach
(„Dwa stany kontra ciagla skala", „Bialy", „Policzyc, nie mierzyc") mimo zwykłej pisowni —
sędzia tego nie liczy (OCR tylko do nazw). Do zrobienia: (a) w `prompt_szablon.py` twarde zdanie
„spell every quoted string EXACTLY, Polish diacritics ą ć ę ł ń ó ś ż ź included" przy KAŻDYM
napisie (nie tylko w constraints), (b) w sędzim kontrola diakrytyków: OCR napisów vs lista
etykiet → flaga lekka „brak ogonków" (nie odrzucenie — obraz i tak wchodzi). Wizualnie:
t02 mapa-mysli i t05 porownanie odsłaniają się czysto (bez ramek, bez rozdarć).
Wdrożone (a): `prompt_szablon.py` — zdanie o dosłownej pisowni z diakrytykami w sekcji „Text in
the image" (każdy napis). (b) kontrola OCR w sędzim — otwarte.

## 6d. Iteracja 7 — „szkielet bez tuszu" (2026-09-02 po południu)

**Werdykt usera na ig6 (klatka 0 t02):** „duży progres, ale dalej widać nieczystości; nie da się wyczyścić do zera;
propozycja: zrezygnować z odsłaniania, zostawić zoom" + pomysł „wszystkie piksele do średniej" (obawa: zgubi strzałki)
+ „napisy dzieliły się na połowy". Diagnoza na maskach ig6 (bez LLM, `scratchpad/iter7/symuluj.py`):

| mechanizm | dowód (t02) | reguła, która to robiła |
|---|---|---|
| składowe sporne między regionami zostają w szkielecie | 2 klamry, 1025 + 929 px | `dolacz_skladowe` krok 1 („strzałka w rynnie → reszta") |
| pył < 0,003 % W·H zdjęty z partycji → w szkielecie | 218 drobin ≤ 47 px: kropki nad „i", ogonki podtytułu, promienie słoneczka, blady łuk | `PYL_UDZIAL` (regiony.py) + sędzia „pył nie zliczany" → werdykt „0 strzępów" mimo brudu |
| blade piksele (16 < odch. ≤ 60) poza progiem tuszu | t01: 25 897 px = wnętrza kropel; t05: skóra figur, biały kwiat, rozeta | `PROG_TUSZU = 60` (nie-tusz = nikt nie przydziela) |

„Napisy na połowy" = ten sam mechanizm: litery idą z obiektem, ich kropki/ogonki (pył) zostają w klatce 0.

**Reguła (domknięcie w `maski()`, regiony.py):** (1) cały tusz bez właściciela → warstwa właściciela najbliższego tuszu
(głosowanie pikseli po `distance_transform_edt`); składowa ≥ pyl_px ciążąca do ≥ 2 regionów (≥ 15 % pikseli) → PÓŹNIEJSZY
region w kolejności odsłaniania; (2) blade piksele ≤ `PROM_SLABE` (5 % W = 77 px; wnętrze kropli t01 max 65 px) od tuszu
właściciela → jego warstwa, dalsze → poza wszystkimi maskami (podkład `#pl-ig-podklad` w kolorze papieru = spłaszczenie);
(3) reszta = piksele w kolorze papieru poza sumą regionów (papier ma odch. ≤ 5 w p99,9; próg 16). Nic z tuszu nie ginie,
plansza kończy jako kompletny obraz; HTML scen bez zmian (te same nazwy plików masek).

**Sędzia:** M7a = piksele nie-papierowe widoczne w klatce 0 (z pyłem i bladymi; ≥ 200 px → ciężki → bramka: obraz w całości),
M7b = nie-papier nigdy nie pokazany (spłaszczony; > 2 % tuszu → ciężki). Wynik na tych samych 7 obrazach
(`przebiegi/20260902-mendel-ig7` = kopia ig6 z nowymi maskami, regiony identyczne):

| scena | preset | M7a ig6 (px) | M7a ig7 | M7b ig7 | werdykt ig6 → ig7 |
|---|---|---|---|---|---|
| t01 | proces:4 | 31 498 | 0 | 0 | ODRZUCONY → UWAGI |
| t02 | mapa-mysli:5 | 4 474 | 0 | 0 | ODRZUCONY → UWAGI |
| t03 | proces:4 | 6 795 | 0 | 0 | ODRZUCONY → UWAGI |
| t04 | proces:3 | 1 247 | 0 | 0 | ODRZUCONY → UWAGI |
| t05 | porownanie | 3 732 | 0 | 0 | ODRZUCONY → UWAGI |
| t06 | centrum-boki | 1 002 | 0 | 0 | ODRZUCONY → UWAGI |
| t07 | proces:3 | 533 | 0 | 0 | ODRZUCONY → UWAGI |

(ig6 „ODRZUCONY" = werdykt NOWEGO sędziego na starych maskach; stary sędzia dawał UWAGI, bo nie liczył pyłu.) Blade piksele
dołączone do warstw: 34–110 tys. px/obraz, spłaszczone 0 px w 7/7 (rozeta t05 poszła z najbliższym obiektem).

**Render ig7:** 7/7 scen (harness, ~6 min równolegle), `przebiegi/20260902-mendel-ig7/FILM.mp4`; strony:
`www/produkcja_PMENDb.html` (7 kolumn, ig6 i ig7 obok siebie), `www/przeglad_ig.html` (92 kafle, ig7 na górze).
Klatki „0" z renderów (po nagłówku, przed pierwszym obiektem) ig6 vs ig7: t01 blade krople → czysty papier; t02 kropki,
klamry, słoneczko → czysty papier; t03/t05 drobiny → czysty papier; klamry t02 wchodzą razem z „samopylność" (k=2).
Dowody: `scratchpad/iter7/k0_ig6_vs_ig7_7scen.png`, `t02_17.5s_ig6_vs_ig7.png`, `t02_52.5s_ig6_vs_ig7.png` (ulotne).

**Pozostałe ryzyka (nie do naprawienia maskami):** etykieta w złym slocie (t02: „osiem roślin" pod tacą wchodzi w k=1, obiekt
w k=5 — tylko OCR to złapie); obiekt wystający poza slot wchodzi w całości z większościowym właścicielem; blada ozdoba do 77 px
od cudzego obiektu wchodzi z nim. Pułapka sesji: pętla zsh `${x:+--flaga $x}` nie dzieli słów → JEDEN argument → argparse
„unrecognized" (grep na „Error" nie łapał małego „error") → tylko t02 dostało maski; `xargs -I{}` z długą ścieżką → „command
line cannot be assembled" → render nie wystartował. Oba naprawione (driver w Pythonie, pętla `for … &; wait`).

## 6e. Iteracja 8 — zakaz nagości + łagodne wejścia warstw (2026-09-02 wieczór)

**Werdykt usera po ig7:** „żeby nie było nagości na images (wtopa!!)" — ig6/ig7 t05: slot „Wzrost czlowieka" opisany przez
agenta jako „rząd ludzkich sylwetek" → gpt-image-2 narysował 7 NAGICH postaci (w tym dzieci); „żeby elementy na img nie
pojawiały się nagle, tylko jakaś lekka tranzycja" — warstwy wchodziły opem `pop` (0,6 s, skala 0,9→1, ease sprężysty).

**Nagość — decyzja usera (po wdrożeniu): „tego zakazu nagości nie wpisuj do promptów, bo zaraz będzie wszystko zakrywać;
może ew. kontrola z flagą i tyle".** Stan końcowy:
1. Prompty BEZ zakazu (cofnięte: bullet w „Hard constraints" `infografika.mjs`, zdanie w `CZYTELNOSC` `prompt_szablon.py`,
   §1a w `INFOGRAFIKA_REGULY*.md`, zdanie w bloku v5). Bez ponawiania i bez blokady obrazu.
2. Kontrola po wygenerowaniu: `fabryka/pilot/kontrola_tresci.py --png X [--json]` — qwen-flash `:8020` (0 $, ~2 s):
   `{ludzie, nagosc, uzasadnienie}`; `infografika.mjs` wypisuje `KONTROLA TREŚCI: ok · postaci N` albo `FLAGA: NAGOŚĆ (postaci: N; …)
   — obraz zostaje, decyzja poza narzędziem`; dziennik `kontrola: {ludzie, nagosc}`. Test na 7 obrazach ig7: t05 NAGOŚĆ
   (7 postaci), 6 pozostałych czyste. Konsekwencja: nagi obraz idzie dalej do planszy, flaga jest tylko w dzienniku/konsoli —
   jeśli ma to kogoś zatrzymać, potrzebna decyzja (np. agent przy `FLAGA: NAGOŚĆ` wpina obraz w całości bez tego slotu, albo
   masówka zbiera flagi do raportu).
3. ig8 t05 wygenerowany na nowo tą samą komendą co agent — UWAGA: jeszcze Z (dziś cofniętym) zakazem w prompcie → 8 postaci
   ubranych za pierwszym razem (76 s, 0,01 $), 3 regiony jak wcześniej, sędzia UWAGI 0 ciężkich, `plansza.mjs` bez zmian.
   Stare pliki: `PMENDb-t05/assets/ig/_ig7/`, dziennik `infografiki.ig7.jsonl`.

**Łagodne wejścia:** snippet `regiony.py` daje `fadeIn dur 1.0` (baza i każdy region; ease `power2.out` z dispatchera
`fabryka/komponenty.mjs`), OPS/ZASADY: „wejścia TYLKO łagodne: fadeIn 0,9–1,2 s albo fadeUp z y ≤ 10, nigdy pop";
blok v5: „nigdy `pop` ani `dur` < 0,8 s — elementy obrazu mają się wyłaniać, nie wyskakiwać". W ig8 (kopia ig7) wszystkie
`pop` na `#pl-ig-r*` zamienione na `fadeIn 1.0` regexem (3 formaty kotwic: `czas(t, d)`, `K("słowo", u)`, `t_r1`).

## 6f. Kickery w języku lektora (2026-09-02 wieczór)

**User:** „wszystko jest po polsku, ale nagłówki po ang”. Źródło: `fabryka/prompty/REZYSER_V3.md` §kicker kazał wprost
„2–5 słów WERSALIKAMI po angielsku, w stylu nagłówka RepoChad” (spadek po śledztwie RepoChad). Kicker z `PLAN.json`
idzie przez `06_produkcja.py` do BRIEF.md/STORYBOARD.md/film.json sceny, a agent wpisuje go dosłownie do `plansza.mjs`.

**Zrobione:** (1) reguła: „kicker w JĘZYKU LEKTORA (transkryptu): polski lektor → polski kicker z polskimi znakami;
nigdy nie mieszaj języków” (edycja bezpośrednia, bez codex1 — jedno zdanie); (2) `produkcje/PMENDb/PLAN.json`: 7 kickerów
przetłumaczonych (oryginały w `kicker_en`): OCZYWISTA ODPOWIEDŹ · NADAJ WYNIKOM ZNACZENIE · KONTROLUJ KRZYŻÓWKĘ ·
PRZEŁĄCZ MECHANIZM · POLICZ, CO SIĘ RÓŻNI · USTAL PUNKT WYJŚCIA · F2 WYDAJE WERDYKT; podmiana dosłowna w plikach
produkcji (BRIEF/STORYBOARD/film.json/meta.json) i w scenach ig8 (plansza.mjs, plansza.t1.mjs, klatki) — 7 różnych
zapisów kickera u agentów (`pl-kick`, `kick`, `tb-kicker`, `Count what <b>differs</b>`, „Ustal punkt wyjścia”…): regex
tolerujący tagi i wielkość liter; pułapka: dopasowanie kończyło się przed `</b>` → `</b></b>` (naprawione) i druga
przebieżka nadpisała `kicker_en` (przywrócone). ig6/ig7 zostają po angielsku (dowody). PWYTB (enzymy) też ma angielskie
kickery — nieprzerabiane (produkcja testowa). Render ig8 po podmianie: 7/7, `FILM.mp4` sklejony, strony (8 kolumn / 106 kafli)
przebudowane; ogonki w wersalikach mono renderują się poprawnie (t01 „OCZYWISTA ODPOWIEDŹ”, t03 „KONTROLUJ KRZYŻÓWKĘ”, t05 „POLICZ, CO SIĘ RÓŻNI”).

## 6g. Walidacja promptów codexem (2026-09-02 wieczór)

**User:** „puść walidację codexa do tych promptów, czy jeszcze nie wyłapie jakichś kwiatków”. Pakiet 8 dokumentów
(REZYSER_V3, PROMPT_T1 szablon, złożone prompty T1/T2 sceny t05, INFOGRAFIKA_REGULY(_BOGATE), gotowy prompt rysownika t05,
snippet warstw t05) + brief recenzenta + strict schema → `codex2 exec -m gpt-5.6-sol high` (31,5k in / 16,6k out, ~6 min).
Materiały: `scratchpad/walidacja/{pakiet.md,brief.md,schemat.json,wynik.json}` (ulotne). Wynik: 30 pozycji.

**Zastosowane (21 poprawek):** REZYSER_V3 — złoty przykład miał 4 angielskie kickery (→ PRAWDZIWE PYTANIE, MALI EKSPERCI
WYGRYWAJĄ, DOWÓD W LICZBACH, SEDNO SPRAWY), antyprzykład opisany także jako „kicker w obcym języku”, doprecyzowanie
„nośnik dominanty przy pierwszym twierdzeniu, liczby przy słowach lektora”, `plan.sceny` = jeden wpis na `[sNN]`,
`wspierajace` = typy modułów (nie teksty), zapełnienie 60–90 % zamiast 85–90 % (zgodnie z T1/T2). PROMPT_T1 — „klatki
wzorca” także gdy ich nie ma (egzemplarz biblioteki), ścieżki względne od «REPO». PROMPT_T2 — usunięte `swap{show:false}`
(łamało „nic nie znika”). Blok v5 masówki — „wklej dosłownie” + obowiązkowa podmiana `«słowo»`, `clip-path` → maski,
dominanta = największy region infografiki (nie osobny HTML), baza = sam papier (nie dokładka), etykiety z ogonkami i nigdy
wersalikami (t05 miał „Bialy”, „ciagla” od agenta). REGULY_BOGATE — 0–8 części wg presetu (było 2–6), leader line tylko
poza schematami slotowymi; REGULY — zakaz wersalików. schematy.py `opis_layoutu` — „no leader lines except where a slot
explicitly asks for one” (pas werdyktu prosił o linię wbrew ogólnemu zakazowi). regiony.py — martwy komentarz o clip-path/
evenodd w nagłówku snippetu; **reguła 6 (sama etykieta → scalenie z obiektem) nie dotyczy slotów presetu** — pas werdyktu
w `porownanie` zostaje własnym, ostatnim regionem (t05: 3 → 4 regiony; ig8 NIE przeliczone, bo plansza ma opy dla r1–r3).

**Fałszywe alarmy / artefakty pakietu:** ścieżki `ig6` w promptach t05 (ig8 to kopia ig6 — prompty są zapisem historycznym),
zdanie o ubraniach w prompcie t05 (z cofniętej już wersji), kolor podkładu `#fcf3e3` ≠ `#F1EAD9` (podkład = zmierzony papier
obrazu, celowo), „baza zawiera resztę obrazu” (po domknięciu reszta = sam papier), `pop` w złożonym prompcie C (blok już
poprawiony). **Nie zastosowane:** przebudowa złotego przykładu (dominanta „10 z 512” przed s03) — zmiana semantyki
przykładu, do decyzji; zmiana nazwy pola `wspierajace` (schemat 06_produkcja.py) — tylko doprecyzowanie opisu.

## 6h. Prezenterka (avatar) + głos kobiecy + pauzy lektora (2026-09-03 rano)

**Pauzy lektora (user: „lektor nienaturalnie przerywa”):** pomiar t02 (m04, 221 s): 47 pauz ≥ 0,45 s, mediana 0,73 s,
22 pauzy ≥ 0,8 s — WSZYSTKIE po kropce/pytajniku, po przecinkach naturalne 0,38 s. Worker na boxie (`/root/tts_worker.py`
w CT111) NIE dzieli tekstu: cały akapit idzie do `model.generate()` — długie pauzy robi sam omnivoice na długim tekście; parametru
pauzy nie ma (tylko `speed`). Głos f06 ma tak samo (58 pauz, mediana 0,73 s, 17 % czasu). User nie chce cięcia i sklejania;
próbka ze skróconymi pauzami (scratchpad) tylko do odsłuchu. Opcje bez sklejania: zaakceptować albo inny silnik (gemini).

**Avatar 2D (moduł, 0 GPU, 0 LLM):** `fabryka/avatar/` — arkusz gpt-image-2 z tą samą postacią w N panelach (v1: 3 stany ust,
panele identyczne co do piksela; v2: 6 wizemów A–F, drobne różnice 8–12 jasności między panelami), `arkusz_na_sprity.py`
(klucz zieleni/alfa, wyrównanie korelacją), `avatar.mjs` (nakładka: sprite'y + op `usta` = `tl.set(data-usta)` seek-safe;
nowy op w `komponenty.mjs`, dozwolony w walidatorze), `14_avatar.py --on|--off --postac --tryb amplituda|rhubarb --strona
--szer --render` (wstrzykuje 1 import + `return zAvatarem(...)`, kopia `plansza.bez_avatara.mjs`). Rhubarb Lip Sync 1.14 (MIT)
zainstalowany w `external_connections/lipsync/rhubarb/` (wiersz w NARZEDZIA.md): `-r phonetic`, 16 s na 220 s audio, 1473 cues.
Głos prezenterki: **f06 „Bright”** (bank; próbki f02/f06/f09 wysłane userowi, bez wyboru → f06); `10_polski_glos.py --glos f06
--force --znacznik m04` przegrał 7 tablic PMENDb (stare m04 obok jako `01.m04.wav`), `12_propaguj_audio.py` wgrał do
`przebiegi/20260902-mendel-ig9-avatar` (kopia ig8). f06 jest wolniejszy: t02 266 s vs 221 s.
Render ig9-avatar 7/7 (v1 + rhubarb 3 stany, 13cqw, prawy róg); **t03: `hyperframes check` text_occluded** (karta `#sup2` w prawym
dolnym rogu) → `--strona lewa`. Porównanie A/B (m04, t02): `przebiegi/_ab-avatar/t02-{v1-rhubarb,v2-rhubarb,v2-amplituda}` +
klip 4 wariantów w scratchpadzie. Pułapka: arkusz v2 przyszedł jako PNG z alfą (nie zieleń) → splitter bez alfy źródła dał
czarne sprite'y (naprawione: alfa źródła ∧ klucz).

**EchoMimicV3 (GPU, Apache-2.0):** instalacja na `gpu_server` (prox0, 3× RTX 6000 Ada 48 GB; 5060 Ti na CT111 ma tylko ~8 GB
wolne przy TTS): `/opt/echomimic/` — uv + Python 3.11 (host bez venv/pip), repo + wagi (Wan2.1-Fun-1.3B-InP 19 GB, transformer
3,4 GB, wav2vec ×2), `run1.sh` = 138 klatek 768² z portretu v1 i 14 s f06 na GPU 2. Wynik: patrz §6i (po zakończeniu).

## 6i. Wyniki eksperymentów z prezenterką (2026-09-03 rano, „masz 5060 — poeksperymentuj ze wszystkim")

**Co porównano (t02, ten sam lektor m04, klip 18–30 s, 4 panele obok siebie — scratchpad `ab_usta_4warianty.mp4`):**
`_ab-avatar/t02-v1-rhubarb`, `t02-v2-amplituda`, `t02-v2-rhubarb` + wcześniejszy v1-amplituda. v1 = 3 stany (zamknięte/pół/otwarte),
v2 = 6 wizemów (A–F). Rhubarb daje kształty zgodne z fonemami (zaokrąglone „O", zęby na „S"), amplituda tylko „jak głośno".
Panele v2 wyszły z alfą zamiast zieleni → dwa bugi splittera naprawione (alfa źródła; krawędź panelu → przezroczysta).
Skalowanie: po WYSOKOŚCI (`wys: 44cqh`), nie szerokości — inaczej węższy arkusz v2 dawał postać na 79 % kadru.

**Film ig9-avatar (7 scen, lektorka f06, v1 + Rhubarb 3 stany, prawy róg; t03 lewy róg):** 7/7 renderów, `FILM.mp4` 803 s
(f06 mówi wolniej: t02 266 s vs 221 s m04), strona `www/produkcja_PMENDb.html` 9 kolumn, `przeglad_ig.html` 120 kafli.
`hyperframes check` złapał kolizję prezenterki z kartą `#sup2` w t03 (text_occluded) → `--strona lewa` = CZYSTY.
Koszt: 0 tokenów LLM (obrazy postaci 2 × 0,01 $), Rhubarb ~16 s/scenę, render jak zwykle.

**EchoMimicV3 (GPU):** zainstalowany na `gpu_server` (uv, py3.11, torch 2.7.1 cu126; wagi 23 GB); 3 próby: run1 768²/138 kl.
OOM (skrypt ładował cały potok ~19 GB na GPU obok vLLM 26 GB), run2 512²/81 kl. OOM (ten sam powód: `GPU_memory_mode`
ignorowany przez `infer_flash.py`), run3 (model_cpu_offload) OOM przy ładowaniu T5 (hook offloadu ładuje 10,6 GB obok transformera/CLIP), **run4 = 384² / 49 klatek /
`sequential_cpu_offload`: DZIAŁA** — 1,96 s wideo w 133 s łącznie (~90 s ładowanie modeli z dysku, reszta generacja), wyjście
`outputs/run4/ref_v1_768_output.mp4` (kopia w scratchpadzie `echomimic_run4_384_2s.mp4`); run5 = 137 klatek (5,48 s) w 211 s łącznie (~90 s ładowanie + ~120 s generacja, czyli około 22 s GPU na 1 s filmu przy 384², 8 krokach,
sequential offload; przy większej wolnej pamięci byłoby szybciej, ale karty zajmuje qwen). Jakość: postać i styl zachowane, lekki ruch
głowy; w run4 (2 s) usta prawie nieruchome (początek nagrania), w run5 wyraźna artykulacja (zęby, różne kształty), ale z artefaktami
wnętrza ust (ciemnozielone plamy) przy 384². Werdykt: technicznie działa, na skalę NIE (minuty GPU na planszę, karty współdzielone,
artefakty wymagałyby wyższej rozdzielczości = więcej VRAM). 2D sprite'y + Rhubarb zostają metodą produkcyjną; EchoMimicV3 = intro/premium. Łatka: `infer_flash.py` linie `pipeline.to(device)` → wybór trybu wg `--GPU_memory_mode`. Wniosek już teraz: **na skalę** GPU-avatar nie wchodzi (minuty GPU na minutę filmu, karty zajęte przez qwena);
to jest kandydat na „tryb premium" lub na stałe intro, nie na każdą planszę.

## 6j. „Odkrywamy koło na nowo” — właściwa technologia: rig sterowany parametrami (2026-09-03)

**User (po obejrzeniu sprite'ów):** animacje słabe; na Twitchu avatary reagują na głos w czasie rzeczywistym i wyglądają lepiej;
wrażenie, że nie znalazłem właściwej technologii. Trafne. Klasa rozwiązań z Twitcha = **rig + parametry** (Live2D Cubism / VRM 3D):
postać ma suwaki (otwarcie i kształt ust, mrugnięcie, kąty głowy, oddech), które głos i szum sterują w czasie — CPU/WebGL,
deterministycznie, w przeglądarce. Sprite'y (nasz „PNGTuber”) i diffusion (EchoMimicV3) to dwa skrajne końce.

**Wybór: VRM + three-vrm w HyperFrames.** Powody: darmowe i standardowe (postać w VRoid Studio suwakami; eksport ma wizemy
A/I/U/E/O, blink, miny), HyperFrames ma adapter Three.js (`hf-seek` → render dokładnie w czasie t, `adapters/three.md`),
jakość VTubera, koszt jak sprite'y (sekundy CPU/GPU-lite na scenę). Alternatywy: TalkingHead (met4citizen, three.js, GLB
z blendshape'ami ARKit/Oculus, `speakAudio` z zewnętrznymi wizemami, `head.animate(dt)` do krokowania — brak Polski w modułach
tekstowych, ale wizemy dajemy z Rhubarba), Live2D/Inochi2D (rig ręczny, nasz arkusz z gpt-image-2 nie wystarczy).

**Spike (w toku):** `fabryka/avatar/avatar3d.mjs` — generator warstwy: importmap (three 0.180 + @pixiv/three-vrm 3.5.5 z jsDelivr),
`<canvas>` w rogu, `<script type="module">`: GLTFLoader + VRMLoaderPlugin, wizemy Rhubarba (A–H, X) → ekspresje `aa/ih/ou/ee/oh`
z przejściem 80 ms, `blink` z deterministycznego harmonogramu (co ~3,7 s), głowa/szyja/kręgosłup z funkcji sinus (kołysanie,
oddech, lekki ukłon przy mówieniu), render tylko z `hf-seek` (bez rAF, bez spring bones → seek-safe). `komponenty.mjs`
`frameShell` przyjmuje `avatar3d` (harness przekazuje `wynik.avatar3d`) — skrypt emituje SILNIK, więc zakaz `<script>` w treści
planszy zostaje. `14_avatar.py --tryb vrm --model X.vrm` kopiuje model do `assets/avatar/model.vrm` i `wizemy.json`.
Model testowy: `fabryka/avatar/vrm/VRM1_Constraint_Twist_Sample.vrm` (pixiv, licencja VRM 1.0, redystrybucja OK, 10 MB;
54 kości, ekspresje aa/ih/ou/ee/oh/blink/happy…). Docelowa postać: user robi w VRoid Studio → jeden plik.
Test: `przebiegi/_ab-avatar/t02-vrm` (kopia ig9 t02, lektorka f06). Wynik → §6k.

**Równolegle (plan B, sprite'y):** arkusz `postac_v3c` z odstępami: 6 paneli, różnice 2,3–3,2 (jak v1) — spójny; runtime `usta`
z crossfade 70 ms + podskok na otwarciu, nowy op `oddech` (scaleY + kołysanie) — klip `ruch_v1_rhubarb_18-30s.mp4` na
`www/avatar.html`. Strona zbiorcza wyników: `www/avatar.html` (`15_avatar_www.py`).

## 6k. Spike VRM — wyniki (2026-09-03, przed południem)

**Działa end-to-end w harnessie:** warstwa Three.js + three-vrm renderuje się w headless Chrome HyperFrames (klatka z ig9 t02:
model pixiv pojawił się w renderze), `hyperframes check` CZYSTY, kotwice bez zmian. Model prezenterki: `three-vrm-girl.vrm`
(pixiv, VRM 0.x, 5,5 MB, licencja VRoid Hub: użytkownik „Everyone”, komercyjne „Allow”; blendshape'y a/i/u/e/o/blink/joy…,
three-vrm mapuje je na aa/ih/ou/ee/oh/blink). User: „ogarnij to z jakąś atrakcyjną dziewczyną i tyle” → ten model na start,
podmiana = jeden plik `.vrm`.

**Pułapki (każda kosztowała jeden render):**
1. Inliner HyperFrames wykonuje skrypty klatki jako zwykłe — `<script type="importmap">` dał `page_error: Unexpected token ':'`
   (JSON czytany jako JS). Rozwiązanie: skrypt klasyczny + dynamiczny `import()` z jsDelivr `+esm`; jedna instancja three przez
   przypięcie wersji, którą sam importuje `@pixiv/three-vrm@3.5.5/+esm` (three 0.185.1).
2. `COMP_ID` z RUNTIME nie jest globalny po inlinowaniu → `page_error: COMP_ID is not defined`; zaczep do timeline'u GSAP bierze
   ostatni z `window.__timelines`, w try/catch.
3. Id klatki zaczyna się cyfrą (`01-p1`) → selektor `#01-p1-av3d` nieważny → canvas bez pozycji renderował się 922×1280 w lewym
   górnym rogu (pierwszy „duch” w klatce). Rozwiązanie: `[id="…"]` jak w silniku.
4. Kadr kamery: pozycja kości `head` to podstawa głowy — kamera na `hp.y+0,03`, cel `hp.y-0,03`, odległość 1,6, FOV 24 → głowa
   z zapasem nad włosami + ramiona (sprawdzone w przeglądarce, `www/media/avatar/vrm_test.html` z suwakiem czasu).
5. Ekspresje: test w przeglądarce (`window.__av3d.test('aa',1)`, `('blink',1)`) — pełne otwarcie ust i zamknięcie oczu działają;
   pierwsze wagi były za słabe (B=zęby to 80 s z 220 s lektora) → podniesione (ih 0,5; ee 0,7+aa 0,35; oh 0,9; ou 1,0).

**Diagnostyka bez renderu:** strona `www/media/avatar/vrm_test.html` (ten sam skrypt warstwy, `hf-seek` z suwaka; `window.__av3d`
z `nazwy()`, `test(n, w)`, `renderAt(t)`) — pętla sekund zamiast minut. Render finalny t02 z dziewczyną: patrz `www/avatar.html`.

## 6l. VRM: ruch v2, modele, film (2026-09-03 południe)

**User po pierwszym VRM:** „wygląda najlepiej z dotychczasowych, ale można lepiej (z omnivoice) i lepiej ogarnąć ruchy — mało
atrakcyjnie”. Trzy przyczyny i naprawy: (1) ruch: czyste sinusy → „szum” z sum sinusów o różnych fazach (głowa, szyja, kręgosłup,
biodra, ramiona), akcenty głowy i pochylenie na energii mowy, sakkady oczu (lookLeft/Right/Up/Down co 1,4–2,8 s), stały
delikatny uśmiech (`happy` 0,22–0,34), mruganie z rozrzutem i podwójnymi, oddech na `spine`/`chest`, **fizyka włosów**
(spring bones krokowane tylko do przodu: render jest liniowy; cofnięcie/skok >0,5 s = reset + 20 kroków rozgrzewki);
(2) usta: kształt z wizemu × siła z **obwiedni głośności lektora** (RMS 25 kl./s z omnivoice, `energia` w `wizemy.json`,
`14_avatar.py energia_glosu`), przejście 90 ms smoothstep; (3) model: pobrane i sprawdzone licencje: **Alicia Solid**
(DWANGO, VRM 0.51, „Everyone”, komercyjne Allow), **Seed-san** (VirtualCast, VRM 1.0, komercyjne „corporation”, credit
wymagany; chłopięca — nie na prezenterkę), three-vrm-girl (pixiv). Pułapka: VRM 1.0 ma odwrócone znaki obrotu ramion
(Seed-san ręce w górę) → `zn = metaVersion==="1" ? -1 : 1`. Kadr: kamera `hp.y+0,06`, odległość 1,7 (kokarda Alicii).

**Film ig9-avatar (VRM, ruch v1 → v2):** pierwszy pełny render VRM: 5/7 CZYSTY, t05 i t07 `text_occluded` (canvas 461×640 px
większy niż sprite) → `--strona lewa` dla t03/t05/t07; drugi render z ruchem v2 dla wszystkich 7 — wynik na `www/produkcja_PMENDb.html`
(kolumna ig9) i `www/avatar.html` (sekcje 2b, 2c; strony testowe `vrm_test_{girl,alicia,seed}.html` z suwakiem czasu).
Porównanie modeli na t02: `_ab-avatar/t02-vrm-girl`, `_ab-avatar/t02-vrm-alicia`.

## 6m. VRM: ruch ciała z GOTOWYCH klipów mocap zamiast ręcznych sinusów (2026-09-03 po południu)

Uwaga usera po ruchu v2: „dalej słaba gestykulacja i lekko potrząsa całym avatarem — nie ma fluidity; zrób lepszy research, bo znowu
odkrywasz koło na nowo, pewnie są gotowe skrypty”. Research w sieci potwierdził: są.

**Co znalazłem (i z czego korzystamy):**
- pixiv/three-vrm, przykład `humanoidAnimation` (`loadMixamoAnimation.js` + `mixamoVRMRigMap.js`): oficjalny retarget klipów
  Mixamo (FBX) na kości znormalizowane VRM — obrót lokalny źródła → obrót w osiach świata względem pozy spoczynkowej → kość VRM;
  VRM 0.x = odbicie x,z kwaternionu; biodra skalowane wysokością. Skopiowane 1:1 do `fabryka/avatar/avatar3d.mjs` (funkcja `retarget`).
- `@pixiv/three-vrm-animation` 3.5.5 (`VRMAnimationLoaderPlugin`, `createVRMAnimationClip`): natywne klipy `.vrma` bez retargetu —
  obsłużone w kodzie, ale na razie bez klipów (pixiv rozdaje 7 póz na BOOTH za kontem; idle/mowa trzeba by nagrać albo kupić).
- Źródła klipów: Ready Player Me `animation-library` (GitHub, LFS; F_Talking_Variations_001–006, F_Standing_Idle*) — **licencja
  restrykcyjna: tylko avatary RPM** → używamy „z gwiazdką”, decyzja usera 2026-09-03 („testujemy wszystko”); three.js
  `examples/models/gltf/Xbot.glb` (rig Mixamo: idle, agree, headShake); J-Beardmore/FreeMotionPack1 (CC0; NodYes1, ShakeHeadNo1);
  Quaternius Universal Animation Library (CC0, 45 klipów darmowych, zip z itch.io — nie pobrany, brak listy nazw); Mixamo
  (konto Adobe usera: „Talking”, „Standing Idle”, „Explaining” — najlepsze źródło klipów mowy, darmowe do użycia w projektach, bez
  redystrybucji plików). Tabela z licencjami: `fabryka/avatar/ruch/LICENCJE.md`.

**Co zmieniło się w kodzie (`fabryka/avatar/avatar3d.mjs` v3, kopia v2: `avatar3d.v2-szum.mjs.bak`):**
- ciało: `THREE.AnimationMixer` na `vrm.scene`, klipy retargetowane; deterministycznie `mixer.setTime(t)` w każdej klatce;
  wagi: spokój = 1−g, mowa = g, gdzie g = bramka energii lektora (okno ±0,25 s, rampa ±0,45 s); warianty klipów zmieniają się na
  onsetach mowy (mowa) albo co 20 s (spokój) z przenikaniem 0,6 s; klipy mowy zapętlone tam-i-z-powrotem (lustrzana połowa
  wpisana w klip), idle zwykłą pętlą;
- z czasu zostały tylko: usta (wizemy Rhubarba × energia), mrugnięcie, uśmiech 0,24, sakkady oczu przez `vrm.lookAt.applier.applyYawPitch`
  (skok 80 ms, do 6°), spring bones krokowane stałym 1/60 s; **żadnych sinusów na kościach** (to one „potrząsały”);
- loadery: `.glb/.gltf` (GLTFLoader), `.fbx` (FBXLoader z jsDelivr +esm), `.vrma` (three-vrm-animation); nazwy kości bez
  prefiksu `mixamorig`/`Armature` mapowane po nazwie; brakujące kości (końcówki palców, oczy) pomijane z ostrzeżeniem;
- konfiguracja: `assets/avatar/ruch.json` `{pliki, spokoj, mowa, tpoza}` pisze `14_avatar.py --ruch rpm|xbot|nod|<pliki> [--spokoj] [--mowa] [--tpoza]`,
  czyta `avatar.mjs` (tryb3d) → `avatar3d.ruch`; bez pliku = ręce w dół, bez ruchu ciała (jak v2 bez szumu).

**Trzy pułapki (kosztowały po jednej iteracji każda):**
1. RPM FBX ma w spoczynku pozę z klatki 0 (ręce w dół), nie T-pozę → retarget liczył deltę od złej bazy (ręce w górę). Rozwiązanie:
   T-poza z osobnego pliku (`Feminine_TPose.fbx` z tej samej biblioteki) jako `tpoza`; automatyczne wykrycie (`czyTpoza`: lewa ręka
   wzdłuż ±X) i ostrzeżenie, gdy plik nie ma T-pozy i nie ma referencji.
2. `THREE.LoopPingPong` + `mixer.setTime(t)`: mixer nie zeruje `_loopCount` akcji między wywołaniami, więc kierunek odtwarzania
   przełączał się co klatkę → poza migała (skok dłoni 38 cm/klatkę). Rozwiązanie: zwykła pętla + lustrzana połowa wpisana w klip.
3. FBX z Blendera (FreeMotionPack1): kości `ArmatureHips`, Armature obrócone −90° X i ×100 → pozycja bioder liczona przez świat
   (obrót i skala rodzica), nie z lokalnego `y`.

**Pomiar płynności (three-vrm-girl, t02, 25 kl./s, `www/media/avatar/vrm_test_v3_girl_rpm.html`, po naprawie pułapki 2):**
głowa: średni krok 1,1 mm/klatkę, maks. 6,8 mm, maks. zmiana kroku 2,9 mm; obrót głowy średnio 0,9°/klatkę, maks. 5,5°;
dłoń: średnio 8 mm, maks. 53 mm/klatkę — dokładnie tyle, ile w klipie źródłowym (5,3 cm na klatkę 30 kl./s). Ten sam czas dwa razy →
identyczna poza (0,00 mm). v2 (sinusy) miało głowę „gładszą” w liczbach (maks. 1,7°/klatkę), ale ruch nigdy nie ustawał (1,4 mm co
klatkę, składowe 4,7× częstotliwości) — to była wibracja, którą user widział; v3 rusza się jak nagranie: gest, pauza, przeniesienie ciężaru.

**Wyniki obrazkowe:** strony testowe `www/media/avatar/vrm_test_v3_{girl,alicia,seed}_{rpm,xbot,nod}.html` (suwak + ▶ 25 kl./s);
render harnessa `przebiegi/_ab-avatar/t02-vrm-girl` i `t02-vrm-alicia` z zestawem `rpm` (wyniki w §6n, gdy skończą się liczyć),
`www/avatar.html` §2d.

**Otwarte:** (1) klipy mowy z czystą licencją — user pobiera z Mixamo (Talking, Standing Idle, Explaining) do `fabryka/avatar/ruch/`
i podaje `--ruch plik,plik`; (2) Quaternius CC0 zip (itch.io) do sprawdzenia rigu; (3) dobór klipów pod „prezenterkę” (mniej
zamaszyste gesty; RPM Talking_003 macha rękami); (4) głowa z klipu vs kamera — klipy patrzą wprost, dobrze; (5) `.vrma` gotowe
w kodzie, brak klipów.

## 6n. Ruch v3 w renderze harnessa — wyniki (2026-09-03, 09:35–09:41)

| scena | model | zestaw klipów | render | check | klip na www |
|---|---|---|---|---|---|
| `przebiegi/_ab-avatar/t02-vrm-girl` | three-vrm-girl (VRM 0.x) | rpm (idle ×2, talking ×4, T-poza) | 3 min 0 s na 266 s planszy, 62 MB | CZYSTY | `www/media/avatar/vrm_v3_girl_30-42s.mp4`, klatki `vrm_v3_klatki.png` |
| `przebiegi/_ab-avatar/t02-vrm-alicia` | Alicia Solid (VRM 0.51) | rpm | 2 min 50 s, 72 MB | CZYSTY | `www/media/avatar/vrm_v3_alicia_30-42s.mp4` |

Oba renderowane po naprawie pułapki 2 (§6m). Wcześniejszy render z `LoopPingPong` przerwany (poza migała) — nie ma go w wynikach.
Po drodze: `14_avatar.py --on` na scenie, która już ma avatar, NIE wstrzykuje drugi raz, ale odświeża `assets/avatar/ruch.json`
i model — więc podmiana zestawu klipów = ponowne `--on --ruch … --render`, bez `--off`.
`www/avatar.html` §2d (tabela zestawów z gwiazdką przy RPM, klipy 30–42 s, pasek klatek, pomiar płynności v2 vs v3, komenda).
Nie zrobione: Quaternius CC0 (itch.io odrzuca klucz pobierania z curl — zip 15 MB do pobrania ręcznie), Mixamo (konto usera),
film 7 scen ig9 z ruchem v3 (na życzenie: `14_avatar.py --on --tryb vrm --ruch rpm --render` na każdej scenie biegu, ~3 min/scena).

## 6o. Research 2 („jeszcze lepiej”) → v3.1: akcenty mowy, kontakt wzrokowy, cienie; opcje większe (2026-09-03, 10:00–10:40)

User po v3: „dużo lepiej, ale chciałbym jeszcze lepiej — zorientuj się w necie”. Co znalazłem i co z tego zrobiłem od ręki:

**Wdrożone w `avatar3d.mjs` v3.1 (kopia v3: `avatar3d.v3-klipy.mjs.bak`):**
- **Akcenty mowy** (wzór: biblioteka TalkingHead — head movement z timingów mowy): szczyty obwiedni energii lektora
  (maksimum lokalne > 0,55 z wybiciem ≥ 0,3 nad minimum w ±0,3 s, odstęp ≥ 0,7 s) → kiwnięcie głową do 2,9° przez 0,32 s
  ADDYTYWNIE na klip (mnożenie kwaternionu głowy po `mixer.setTime`) + uniesienie brwi (ekspresja „Surprised” 0,10) na mocnych.
  t02: 231 akcentów na 266 s (0,87/s). Bez kryterium wybicia było 402 (1,5/s) = kiwanie bez przerwy — odrzucone.
- **Kontakt wzrokowy** (wzór: TalkingHead `makeEyeContact`): mówi → oczy w kamerę + mikrosakkady ±2°; pauza → co drugi raz
  zerknięcie w bok/dół do 8°; zmiana celu 110 ms; przy skoku > 4° mrugnięcie. Segmenty co 1,7 s, deterministycznie z hasza.
- **Amplituda gestów** `RUCH.silaGestu` (domyślnie 0,85): klip mowy nigdy nie dostaje pełnej wagi, reszta = idle → gesty
  spokojniejsze (RPM Talking_003 machał rękami). Uśmiech 0,26 w pauzach, 0,16 przy artykulacji.
- **Cienie** `PCFSoftShadowMap` (klucz 2048², bias −0,0004, normalBias 0,015): cień włosów na twarzy i szyi = głębia; MToon je odbiera.
  Światła: hemi 1,25, klucz 1,9 z góry-przodu-boku, fill 0,55, rim 1,0. Obrysy MToon były włączone (12 materiałów z obrysem).
- Płynność po zmianach (głowa, 10–40 s): średnio 0,99°/klatkę, maks. 5,8°, maks. zmiana 1,8° — jak v3 (nod nie szarpie).
- Strona zbliżeniowa do oceny twarzy: `www/media/avatar/vrm_test_v3_closeup.html` (wys 700, kamera na twarz).

**Znalezione, NIE wdrożone (opcje na później, z licencjami):**

| co | co daje | koszt / haczyk | licencja |
|---|---|---|---|
| **met4citizen/TalkingHead** (JS, three.js) | gotowa prezenterka: lip-sync z timingów słów, gesty (`playGesture`: handup, index, ok, thumbup, shrug…), nastroje, kontakt wzrokowy, idle, Mixamo FBX | wymaga avatara z 52 ARKit + 15 wizemów Oculus (Ready Player Me / Avaturn / MPFB), styl „realistyczny” nie anime; pętla real-time (`animate(dt)`), brak seek → do HyperFrames trzeba by krokować własnym zegarem | kod MIT; przykładowe avatary CC BY-NC / niekomercyjne; RPM avatar własny = licencja RPM |
| **NVIDIA Audio2Face-3D** (open-source 08/2025) | audio → 52 blendshape’y ARKit per klatka + emocje (twarz jak z filmu) | GPU (gpu_server), pipeline NIM/docker; VRoid ma tylko 5 wizemów + presety → potrzebny avatar z ARKit (RPM/Avaturn) albo mapa ARKit→VRM (stratna) | kod Apache-2.0, modele NVIDIA Open Model License |
| **PantoMatrix / EMAGE** (CVPR 2024) | audio → pełne ciało + twarz (SMPL-X + FLAME), gesty zsynchronizowane z treścią mowy, nie tylko z rytmem | GPU; wyjście SMPL-X → konwersja do BVH/Mixamo (jest w repo: „transfer to bvh/ARKit”) → nasz retarget; dane BEAT2 CC BY-NC → wagi prawdopodobnie NC | kod: brak jasnej licencji w README; dane NC |
| **DiffSHEG** (CVPR 2024) | audio → gesty (BVH, szkielet BEAT 34/SHOW 88) + ekspresje (JSON), real-time | GPU; wyjście BVH → `BVHLoader` + mapa kości BEAT→VRM (do napisania); trenowane na BEAT/SHOW (NC dane) | repo BSD-3 wg wyszukiwarki, README bez licencji — sprawdzić przed produkcją |
| **GestureLSM (ICCV 2025), TANGO (ICLR 2025)** | nowsze generatory gestów z audio, wagi na HF/GDrive | jak wyżej; TANGO = retrieval z wideo (inny paradygmat) | sprawdzić |
| **Mixamo** (konto Adobe usera) | najlepsze klipy „Talking”, „Explaining”, „Standing Idle”, „Happy Idle” z czystą licencją | 10 min klikania usera; pliki do `fabryka/avatar/ruch/`, `--ruch a.fbx,b.fbx --spokoj … --mowa …` | użycie w projekcie tak, redystrybucja nie |
| **Lepsze modele**: ToxSam/open-source-avatars (kolekcje CC0: 100Avatars, ToxSam…, CC-BY: VIPE Heroes), madjin/vrm-samples `fem_vroid.vrm` (licencja niejasna), VRoid Hub (konto) | ładniejsza / inna postać bez zmiany potoku (`--model`) | VRM w rejestrze są stylizowane, nie „anime girl”; VRoid Hub wymaga konta; własny model: VRoid Studio (darmowy, 20 min) | per model |
| VU-VRM (Automattic) | lip-sync VRM z mikrofonu | real-time, głośnościowy — nic ponad to, co mamy | brak licencji w README |

**Ocena:** z tego, co jest gotowe i wolne od GPU, wycisnęliśmy prawie wszystko (mocap + akcenty + wzrok + cienie). Kolejny
skok jakości to gesty generowane z treści mowy (EMAGE/DiffSHEG na gpu_server) albo twarz z Audio2Face-3D — obie ścieżki wymagają
avatara/rigu innego niż VRoid albo konwerterów, i obie mają dane NC w tle. Najtańszy realny krok: klipy Mixamo od usera + własny
model z VRoid Studio.

## 6p. „Inne techniki”: gesty i twarz generowane Z GŁOSU (DiffSHEG na gpu_server) + retarget w świecie (2026-09-03, 12:40–13:30)

User po v3.1: „lekko lepiej, ale do akceptacji długa droga, próbuj dalej, może inne techniki”. Wybrałem technikę o najwyższym suficie
z research 2: **generator gestów z audio** (DiffSHEG, CVPR 2024, kod BSD-3) — gesty i mimika zsynchronizowane z tym, co mówi lektorka,
a nie tylko z rytmem. Działa, wynik jest widocznie żywszy niż klipy idle/talking.

**Spike na `gpu_server` (`/opt/diffsheg`)**
- venv `uv --python 3.10`, torch 2.7.1 cu126 (zamiast 1.13/cu117 z README — Ada nie lubi cu117), `mmcv==1.7.2` (lite; wymaga
  `setuptools` + `--no-build-isolation`; `mmcv-lite==1.7.2` nie istnieje), `transformers 4.40`, `librosa 0.10`, `numpy<2`.
- checkpoint 3,5 GB z Google Drive (`gdown <id>`, bez `--fuzzy` — stara wersja); rozpakowany do `DiffSHEG/checkpoints/beat/<name>/model/fgd_best.tar`;
  `data.tar.gz` z repo → `/opt/diffsheg/data` + symlink `DiffSHEG/data` (runner czyta względnie od CWD).
- skrypt `DiffSHEG/run_t02.sh <wav>` (audios/<wav>, GPU 2, HuBERT z HF do `/opt/diffsheg/hf`); 40 s audio ≈ 10 s, 266 s ≈ 60 s GPU.
- wyjście: `results/beat_34/test_custom_audio/<name>/fixStart4/BestFGD_e999_ddim25_lastStepInterp/pid_{2,4}/gesture/bvh/<stem>.bvh`
  (szablon BEAT: 74 stawów, nazwy Mixamo-owe + Spine3/Neck1, **15 kl./s mimo „Frame Time 0.008333” w nagłówku**) i
  `expression/face_json/<stem>.json` (51 wag ARKit, 15 kl./s). pid = styl mówcy z BEAT (2 i 4).

**Zmiany w `avatar3d.mjs` (v3.2; kopie: `avatar3d.v31.mjs.bak`)**
- `.bvh` przez `BVHLoader` z własną sanityzacją (CR, puste linie, `Frames:` większe niż liczba klatek — próbka BEAT z repo jest ucięta;
  `RUCH.bvhFps` nadpisuje Frame Time), tory `.bones[X].quaternion` → `X.quaternion`.
- **Retarget przepisany na przestrzeń świata**: klip próbkowany na szkielecie źródła (mixer), dla każdej zmapowanej kości
  delta = W_anim·W_rest⁻¹, lokalny VRM = W_rodzicaVRM⁻¹·delta. Dokładny także przy kościach bez mapy w środku łańcucha
  (BEAT: Spine2/Neck pomijane, ich obrót i tak wchodzi w dzieci). Poprzedni wzór z three-vrm (lokalny × rest rodzica) przy
  pominiętym Spine/Neck odchylał głowę do tyłu. Regresja RPM/Xbot: bez zmian.
- biodra: DELTA od pozycji spoczynkowej, skala = wysokość bioder nad stopą (BVH ma korzeń w (0,0,0)); dla generatora tylko ruch pionowy (dryf w poziomie ucięty).
- `RUCH.wygladz` (domyślnie 3 klatki dla BVH z generatora): ważona średnia kwaternionów ±r — szwy okien DiffSHEG (34 klatki, nakładka 4 → co 2 s + 0,27 s)
  dawały skoki dłoni 30 cm/klatkę; po wygładzeniu ok. 25 cm w najgorszym szwie (do dalszej pracy: wygładzanie świadome szwów albo limit prędkości kątowej).
- `RUCH.twarz` (JSON ARKit) → presety VRM: browInnerUp/OuterUp → Surprised (≤0,35), browDown → angry (≤0,25), mouthSmile → happy (0,12–0,5),
  eyeSquint → relaxed (≤0,3); usta nadal z Rhubarba (fonetycznie, po polsku). `RUCH.akcenty=false` wyłącza kiwnięcia z energii, bo głowę daje generator.
- `14_avatar.py --ruch diffsheg [--styl 2|4]`: wav sceny → mono 16 kHz → scp → `run_t02.sh` → BVH + JSON do `assets/avatar/ruch/`, `ruch.json`
  `{bvhFps:15, twarz, akcenty:false}`. Zero zmian w planszy.

**Wynik potoku na scenie (`14_avatar.py --ruch diffsheg --styl 2 --render`, 13:01–13:10):** wav sceny (266 s) → serwer → BVH 12,7 MB
+ JSON twarzy 8,1 MB w `assets/avatar/ruch/` → render 3,5 min, check CZYSTY; klip `www/media/avatar/vrm_dsh_girl_30-42s.mp4`, klatki
`vrm_dsh_klatki.png`, `www/avatar.html` §2f. Po naprawie szwów maks. krok dłoni 48–59 mm/klatkę (wcześniej 280 mm), 24 szwy na 40 s.
Alicia z tym samym BVH: działa (`vrm_test_v3_alicia_dsh2.html`).

**Licencje (z gwiazdką, jak ustaliliśmy):** kod DiffSHEG BSD-3 (OK komercyjnie), ale wagi trenowane na BEAT (CC BY-NC 4.0) — status
wag do wyjaśnienia przed użyciem w produkcie; próbka `beat_2_scott_0_1_1.bvh` = dane BEAT, tylko test. `fabryka/avatar/ruch/LICENCJE.md`.

## 6q. „Skupmy się na alternatywach”: Live2D + mówiąca głowa z portretu (Ditto) (2026-09-03, 13:10–14:00)

User odrzucił kierunek VRM (także z DiffSHEG: „jeszcze gorzej”). Trzy alternatywy zbadane, dwie zbudowane:

**A. Live2D (technika 2D VTuberów z Twitcha) — ZBUDOWANE, 0 GPU**
- modele: próbki Live2D Inc. z `Live2D/CubismWebSamples` (Hiyori, Haru) → `fabryka/avatar/live2d/`; licencja: Free Material License +
  Sample Model Terms (komercyjnie do 10 mln JPY sprzedaży rocznie, credit „This content uses sample data owned and copyrighted by Live2D Inc.”,
  Hiyori bez zmian designu). Runtime w `live2d/lib/`: pixi.js 6.5.10 (MIT), pixi-live2d-display 0.4.0 (MIT), Live2D Cubism Core (darmowy, proprietary).
- `fabryka/avatar/avatar_live2d.mjs` (`live2dWarstwa`): ładuje lib przez `<script>` z assets (żadnych importmap), `Live2DModel.from(..., {autoUpdate:false})`,
  wyłącza `eyeBlink`/`breath` biblioteki; **własny sampler motion3.json** (segmenty liniowe/bezier/schodkowe, pętla) — menedżer ruchu biblioteki
  chodzi na zegarze czasu rzeczywistego, więc nie nadaje się do renderu z czasu; parametry ustawiane w zaczepie `beforeMotionUpdate`
  (przed `saveParameters`, więc fizyka i pose je widzą), `model.deltaTime/elapsedTime` ustawiane ręcznie → fizyka włosów krokowana dt;
  idle: warianty co 10 s z przenikaniem 0,6 s, obroty głowy z klipów tłumione ×0,5 (`tlumGlowa`); z lektora: usta (Rhubarb → ParamMouthOpenY/Form),
  kiwnięcia i brwi na akcentach, spojrzenie z zerknięciami, mrugnięcia, rumieniec w mowie, kołysanie ciała.
- `komponenty.mjs`: `avatar3d.silnik === "live2d"` → `live2dWarstwa`; `avatar.mjs`: `assets/avatar/live2d.json` ma pierwszeństwo przed VRM;
  `14_avatar.py --tryb live2d --postac2d Hiyori|Haru --kadr 0.42 --tlum 0.5 [--wys] [--strona] --render`.
- strony testowe: `www/media/avatar/live2d/test_{hiyori,haru}{,_closeup}.html`; scena: `przebiegi/_ab-avatar/t02-live2d-hiyori` (z `plansza.bez_avatara.mjs`).

**B. Ditto (antgroup, ACM MM 2025, Apache-2.0) — mówiąca głowa z jednego portretu + audio, GPU**
- `gpu_server:/opt/ditto` (venv py3.10, torch 2.7.1 cu126, tryb PyTorch: `checkpoints/ditto_pytorch` + `ditto_cfg/v0.4_hubert_cfg_pytorch.pkl`,
  2,2 GB z HF `digital-avatar/ditto-talkinghead`); pułapki: `huggingface_hub` bez CLI → `snapshot_download` z pythona; brak `cython`/`mediapipe`;
  mediapipe wymaga systemowego `libGLESv2` (`apt-get install libgles2 libegl1 libgl1`). `run_ex.sh <wav> <obraz> <mp4>` (GPU 2).
- portrety: fotorealistyczny z gpt-image-2 (`imagegen render`, prompt w scratchpadzie, 1:1) i anime (`ref_v1_768.png`).

**Wyniki (13:20–13:35):**
- Live2D: `t02-live2d-hiyori` render 3,5 min, check CZYSTY, klip `www/media/avatar/live2d_hiyori_30-42s.mp4`, klatki `live2d_klatki.png`;
  w klatkach: usta na wizemach, mrugnięcia, uśmiech z zamkniętymi oczami i gest rąk z klipów idle, kołysanie; zero GPU.
- Ditto: `example.mp4` (portret malowany z repo) 15,8 s → ok. 40 s GPU łącznie z ładowaniem modelu; `foto_t02.mp4` (1536×1024, 40 s) i
  `anime_t02.mp4` (768², 40 s) po ~50 s każdy w trybie PyTorch (GPU 2, ~1× czasu audio; TensorRT dałby real-time). Twarz mówi, głowa się rusza,
  mimika naturalna; działa i na fotorealistycznym, i na ilustrowanym portrecie. Kompozyt „kamerka” w rogu: `ditto_{foto,anime}_28-40s.mp4`
  (baza: `przebiegi/_ab-avatar/t02-bez-avatara`, maska zaokrąglona 480², ffmpeg `alphamerge`+`overlay`).
- Integracja Ditto z HyperFrames (do zrobienia, ~1 h): 14_avatar.py `--tryb ditto --portret x.png` → wav sceny na serwer → mp4 → `<video>` w rogu
  planszy przez framework-owned media (hyperframes-core), maska CSS `border-radius`; koszt ~1 min GPU na minutę filmu.

**C. Płatne API (punkt odniesienia):** HeyGen Avatar IV ≈ 20 kredytów/min ≈ 1 USD/min (Pro 99 USD = 100 min), D-ID ≈ 2 USD/min.

## 6r. Alternatywy, iteracja 2: „sztywno i mało fps” (2026-09-03, 13:40–14:20)

User: pierwsza iteracja obu alternatyw za sztywna i sprawia wrażenie niskiego fps. Research + poprawki:

**Diagnoza „mało fps”:** (1) Ditto generuje 25 kl./s, a film ma 30 → dublowanie co piątej klatki = szarpanie; (2) w Live2D v1 część sygnałów
procedur była SCHODKOWA (cel głowy z hasza co 2,1 s bez wygładzenia, ParamAngleZ z hasza co 0,7 s) → skoki 8–14° między klatkami.
Jednostki czasu w pixi-live2d-display sprawdzone w źródle: `deltaTime`/`elapsedTime` w ms, fizyka dostaje sekundy — było poprawnie.

**Live2D v2 (`avatar_live2d.mjs`, kopia v1 `avatar_live2d.v1.mjs.bak`):**
- filtry stanowe: sprężyna krytycznie tłumiona (`spr`, τ 0,10–0,4 s) i wygładzanie wykładnicze (`wyg`, osobny atak/opadanie) — krok do przodu,
  cofnięcie/skok > 0,5 s = reset (jak fizyka); SUMA klip+gest+procedury dla ParamAngleX/Y/Z, BodyAngle, ramion i oddechu przechodzi przez sprężynę
  → maks. skok/klatkę: AngleX 8,8°→1,7°, AngleY 11,9°→1,5°, AngleZ 14,2°→1,8° (pomiar 10–22 s, 30 kl./s);
- „wędrujące” cele głowy w mowie (nowy cel co 0,7–1,6 s z hasza, yaw ±8°, pitch ±4,5°, roll ±3°, amplituda × bramka mowy) — wzór VTube Studio
  („voice → any parameter”), bez sinusów; kiwnięcia i brwi na akcentach jak dotąd;
- gesty: klipy modelu ≤ 2,5 s (Hiyori m07–m09, 1,6–2,1 s: ruchy rąk) odpalane na akcentach o sile > 0,75 z odstępem ≥ 4 s, tylko ciało/ręce/włosy,
  wejście/wyjście 0,3 s; idle = klipy > 2,5 s, warianty co 10 s, przenikanie 0,6 s, głowa z klipu ×0,6;
- usta: VTube-Studio-style — otwarcie z wizemu × obwiednia energii (atak 30 ms, opadanie 70–120 ms), forma z klasy wizemu; modele z parametrami
  samogłosek (Kei_vowels z `Live2D/CubismWebMotionSyncComponents`: ParamA/I/U/E/O) dostają mapę Rhubarb→samogłoski (D→A, C→E, B/G→I, E→O, F→U);
- uśmiech oczu (ParamEyeLSmile/RSmile) w pauzach, rumieniec w mowie, oddech syntetyczny gdy klip go nie ma.
- modele: Hiyori (główny), Haru, Kei_vowels/Kei_basic (duża głowa, styl inny — jako test samogłosek; kadr `--kadr 0.9`).

**Ditto v2:**
- `run_ditto2.py` (parametry setup/run jako JSON + `--ctrl plik.json`); `fabryka/avatar/ditto_ctrl.py`: z wav liczy program głowy (25 kl./s):
  wędrujące cele + kiwnięcia na akcentach przez sprężynę → `ctrl_info[fid] = {delta_pitch, delta_yaw, delta_roll}` w STOPNIACH (motion_stitch.ctrl_motion
  dodaje do pozy generatora; jest też `alpha_*`, ale mnoży surowe biny — nie używać);
- setup: `emo 4` (przegląd 0–7 na 8 s: różnice minimalne, `www/…/sweep`), `smo_k_d 2` (mniej wygładzania = żywiej), `crop_scale 2,8`, `crop_vy_ratio −0,05`
  (ramiona w kadrze, 1024×682), `max_size 1024`;
- kompozyt: `minterpolate` 25→30 kl./s (mci/aobmc/bidir) zamiast dublowania klatek; karta 560×420 z zaokrąglonymi rogami.

**Wyniki iteracji 2 (14:00–14:15):**
- Ditto v2: `foto_v2.mp4` 1024×682 i `anime_v2.mp4` 768² po ~60 s GPU każdy; kompozyty `www/media/avatar/ditto_v2_{foto,anime}_28-40s.mp4`
  (karta 560×420; dla źródeł kwadratowych skala po szerokości), pełne 40 s `ditto_v2_*_t02.mp4` (30 kl./s po minterpolate). Program głowy: 29 akcentów na 40 s.
- Live2D v2: trzy rendery `t02-live2d-hiyori` (v2: sprężyny; v2b: uśmiech oczu 0,5→0,26, bo Hiyori zamykała oczy w łuk; v2c: twarz WYŁĄCZNIE z lektora —
  klipy gestów zamykały oczy przez ParamEyeLSmile, plus yaw ±8°→±6°, bo większe wyrzucało fizykę kucyków), każdy 3,5 min, check CZYSTY;
  klip `live2d_v2_hiyori_30-42s.mp4`, klatki `live2d_v2_klatki.png`; www §2h (v1 obok v2).
- Otwarte: Hiyori nie ma klipów „mówienia” — gesty to 3 krótkie klipy z próbki; własny model z Cubism Editor (albo zakup na nizima) dałby więcej i lepiej dopasowanych;
  Ditto: `ctrl_info` można rozszerzyć o `delta_exp` (mimika) i fade; TensorRT dałby real-time (obecnie ~1,5× czasu audio w PyTorch).

## 6s. Alternatywy, iteracja 3: „włosy latają, ust nie widać”, „Ditto dalej sztywno”, lepsze modele (2026-09-03, 14:15–15:00)

**Live2D — diagnoza i naprawa:**
- „Włosy latają wszędzie”: fizyka próbek Live2D (Hiyori: 11 ustawień, wejścia AngleX/Z i BodyAngle; Mao: 16 ustawień, Fps 30) jest nastrojona pod delikatny
  ruch real-time; nasz ciągły ruch głowy z mowy ją przesterowywał. Naprawa: skalowanie wag wejść fizyki w rigu (`im.physics._physicsRig.inputs[].weight × FIZ`,
  domyślnie 0,45; Hiyori: 34 wejścia) + wolniejsze/ciaśniejsze wędrowanie głowy (±4,5°/±3,5°/±2°, sprężyny 0,26–0,45 s) + mniejsze kołysanie ciała.
  Wcześniejsze próby (v2b–v2d) tylko zmniejszały amplitudę — to nie wystarczało, bo problem siedział w wagach fizyki.
- „Ruchu ust nie widać”: Hiyori ma małe usta, a otwarcie było ×(0,55+0,6·energia) z opadaniem 70 ms → usta ledwo drgały. Teraz siła 0,7–1,3 (pełne
  otwarcie na samogłoskach), opadanie 100 ms; pomiar 10–20 s: średnie otwarcie 0,39, > 0,5 przez 29 % klatek mowy. Dodatkowo kadr bliżej (`--kadr`).
- Twarz nigdy z klipów (ParamEyeLSmile/RSmile, brwi, policzki wyłączone z gestów — klipy gestów zamykały oczy w łuk).
- Nowy model: **Mao** (Niziiro Mao, próbka Live2D Inc.; 132 parametry, 8 klipów mtn/special 3,5–9,4 s, 8 ekspresji, fizyka Fps 30, ParamA) —
  inny styl (czarownica w kapeluszu), żywszy rig. Rendery: `t02-live2d-hiyori` (v3) i `t02-live2d-mao`.
- Kei_vowels z Motion-sync: rig samogłosek działa (ParamA/I/U/E/O sterowane z Rhubarba), ale styl (duża głowa, biało-włosy) nie na prezenterkę.

**PRZYCZYNA ŹRÓDŁOWA „włosy latają / nie widać mimiki” w RENDERZE (a nie w podglądzie) — znaleziona 14:40:**
1. HyperFrames przy każdej klatce seekuje timeline GSAP DWA RAZY: `tl.totalTime(t + 0.001, true)` i potem `tl.totalTime(t, …)` (runtime, adapter GSAP),
   a mój zaczep `onUpdate` wołał `renderAt(tl.time())`. Warstwa Live2D v2/v3 (i wcześniej VRM) traktowała KAŻDE cofnięcie czasu jako „nowa sekwencja”:
   reset wszystkich sprężyn (skok do celu) i krok fizyki 1/25 od stanu — czyli w renderze NIE BYŁO żadnego wygładzania, a fizyka włosów dostawała skokowe
   wejście co klatkę. Podgląd w przeglądarce (seek rosnący) tego nie pokazywał, stąd rozjazd między moimi pomiarami a filmem.
   Naprawa: `renderAt` idempotentny dla |Δt| < 1e-4 i bez kroku dla cofnięć ≤ 0,1 s; reset tylko przy skoku > 0,5 s lub cofnięciu > 0,1 s (licznik `DIAG`).
   Test w przeglądarce z wzorcem HyperFrames (t+0,001, t): 300 cofnięć, 0 resetów, maks. skok AngleX 1,3°/klatkę.
2. Parametry, których klip nie ustawia (brwi, forma brwi), dodawałem addytywnie (`PD`) do wartości z poprzedniej klatki — Cubism trzyma ją przez
   saveParameters/loadParameters → akumulacja → brwi na stałe 1,0 (bez ruchu). Naprawa: baza = wartość z klipu w tej klatce albo domyślna modelu; twarz ustawiana absolutnie.
3. Mao nie ma `ParamMouthOpenY` (framework dopisuje nieznane id NA KOŃCU listy, `getParameterIndex` zwraca indeks ≥ liczby parametrów) — usta przez `ParamA` (LipSync group).
   Sprawdzanie istnienia: `index < getParameterCount()`.
Rendery v4 (`live2d_v4_{hiyori,mao}_30-42s.mp4`, paski 8 kolejnych klatek `*_kolejne.png`) — pierwsze z poprawną ciągłością w renderze.

**Mówiąca postać — lepsze modele (research):**
- **JoyVASA** (jdh-algo, MIT): audio → mimika + ruch głowy dyfuzją (DiT), render LivePortrait; `cfg_scale` = intensywność. Instalacja `gpu_server:/opt/joyvasa`
  (uv py3.10, torch 2.7.1 cu126, wagi: `jdh-algo/JoyVASA`, `facebook/wav2vec2-base-960h`, `KwaiVGI/LivePortrait`). Pułapka: `requirements.txt` podmienia torch na build
  z ROCm (`libamdhip64.so: cannot enable executable stack` na glibc 2.41) → reinstalacja torch cu126 po requirements; wyrzucić bitsandbytes/jax.
- **Klip bazowy + lip-sync (schemat HeyGen):** naturalny ruch ciała/głowy z generatora wideo RAZ na postać (EchoMimicV3 = Wan 1.3B, `/opt/echomimic`, run6:
  portret foto + 10 s audio, 512², 249 klatek, 8 kroków, sequential offload), zapętlony tam-i-z-powrotem do długości audio, a Ditto podmienia usta i mimikę per film
  (`/opt/ditto/run_baza.sh <baza.mp4> <wav> <out.mp4>`; Ditto przyjmuje wideo jako źródło: `template_n_frames`, `drive_eye` auto=false dla wideo). Koszt per film bez zmian.
- **Klasa Wan 14B** (Wan2.2‑S2V‑14B, InfiniteTalk, OmniAvatar‑14B; OmniAvatar‑1.3B lżejszy): najwyższa jakość (ciało, gesty, kamera), ale 25–36 GB VRAM
  i minuty GPU na sekundy filmu (S2V: ~4 min na klip 480p na H100) → tylko intro/premium; nasze GPU mają 12–22 GB wolnego obok vLLM/TTS. OmniAvatar: brak pliku LICENSE.
- Sonic (Tencent) — bazuje na SVD (licencja Stability niekomercyjna) → nie.

**Wyniki mówiącej postaci (14:30–14:45):**
- JoyVASA: `animations/prezenterka_foto_t02_40s.mp4` 512², 40 s, ~2 min łącznie z ładowaniem (GPU 1); mimika bogatsza niż Ditto (brwi, szersze usta, obroty głowy);
  kompozyt `www/media/avatar/joyvasa_foto_28-40s.mp4`, pełne `joyvasa_foto_t02.mp4`.
- Klip bazowy EchoMimicV3: run6, 512² → 624×416, 249 klatek (10 s), 8 kroków, sequential offload, **680 s GPU** (GPU 2 przy ~22 GB wolnego); wygląda naturalnie
  (ruch głowy, uśmiech). Ditto na bazie (pętla tam-i-z-powrotem do 40 s, `smo_k_s 5`): 40 s w ~60 s; usta/mimika z audio na ruchu z generatora —
  `ditto_v3_baza_28-40s.mp4`, `echomimic_baza_10s.mp4`. Koszt per film jak Ditto; klip bazowy raz na postać (~11 min GPU na 10 s).

## 6t. „Zrób porządny research, po co odkrywasz koło” — gotowce (2026-09-03, 14:50–15:30)

User odrzucił JoyVASA, EchoMimic+Ditto („dramat”) i Live2D v4 („bez mimiki”). Research pod kątem KOMPLETNYCH, gotowych rozwiązań, nie własnej animacji:

| gotowiec | co daje | koszt / wymagania | licencja | stan u nas |
|---|---|---|---|---|
| **Wan2.2‑S2V‑14B w ComfyUI** (oficjalny workflow „speech to video”) | z portretu + audio: kinowa jakość, naturalna mimika, ruch ciała i kamery; 16 kl./s, 480p (832×480 / 640²), chunk 77 klatek = 4,8 s, rozszerzanie łańcuchem | 25 GB VRAM (fp8) — u nas tylko GPU2 (~22 GB wolne obok llama-server); minuty GPU na 5 s klipu; LoRA lightx2v 4 kroki = szybciej, gorzej | Apache‑2.0 | **modele już w puli `/models8tb/comfy/models`** (s2v fp8 + bf16, wav2vec2, umt5, VAE); CT124 (comfy-pid, GPU2) uruchomiony, `/opt/s2v_run.py` (API `/prompt`) — próbka 640², 77 kl., 20 kroków w toku |
| **LongCat‑Video‑Avatar 1.5** (Meituan, maj 2026) | „production-ready” talking avatar z jednego zdjęcia, Whisper‑Large lip‑sync, nieskończona długość, dubbing, style anime | ~75 GB wag, referencja: 2 GPU + int8; 8 kroków (dystylacja) | **MIT** | nie instalowane (VRAM) — kandydat nr 1, gdy zwolni się karta |
| **LatentSync 1.6** (ByteDance) | lip‑sync na GOTOWYM wideo (twarz 512²), dyfuzja; jakość > MuseTalk | 18 GB VRAM; 25 kl./s; ~czas rzeczywisty×2 | Apache‑2.0 | instalacja `gpu_server:/opt/latentsync` w toku |
| **Kling Avatar / Lip‑sync API** | najwyższa jakość komercyjna z obrazu + audio | 0,052–0,104 USD/s (3–6 USD/min); lip‑sync 0,21–0,42 USD/klip | komercyjna | brak konta (KONTA.md) |
| **Hedra Character‑3 API** | ekspresyjny talking head z obrazu + audio | 2,5¢/s (540p), 5¢/s (720p) → 1,5–3 USD/min | komercyjna | brak konta |
| **HeyGen Avatar IV** | standard rynku | ~1–2 USD/min | komercyjna | brak konta |
| **Open‑LLM‑VTuber / AIRI / ChatVRM** | kompletne AI‑VTuber: Live2D/VRM + emocje z tagów LLM (ekspresje exp3) + lip‑sync z audio | CPU, real‑time (przechwytywanie ekranu, nie render klatka po klatce) | MIT | to samo, co nasza warstwa (pixi-live2d-display); mimika = EKSPRESJE modelu (Hiyori ich nie ma; Mao ma 8) |
| **TalkingHead (met4citizen) + Ready Player Me** | gotowa mimika 52 ARKit, nastroje, gesty, wzrok | real‑time (nagrywanie), styl RPM; VRoid → ARKit przez HANA Tool (Unity) | MIT | nie budowane |

**Wynik Wan2.2‑S2V (15:07–15:16):** `s2v/prezenterka_640_77_00001_.mp4` — 640², 77 klatek (4,8 s, 16 kl./s), 20 kroków, cfg 6, uni_pc, fp8: **555 s na GPU2**
(w tym ładowanie 14B; VRAM do 44 GB łącznie z llama-server). Jakość: naturalna mimika (uśmiech, brwi, kształty ust), obroty głowy, spójna tożsamość, ciągłość klatek —
klasa HeyGen. Kompozyt `www/media/avatar/s2v_kompozyt_28-33s.mp4`, klip `s2v_prezenterka_5s.mp4` (30 kl./s po minterpolate), klatki `s2v_klatki.png`.
Koszt: ~115 s GPU na 1 s filmu (bez LoRA); z LoRA lightx2v 4 kroki ~5× szybciej kosztem jakości. Dłuższe klipy: łańcuch chunków (`ref_motion`).

**Wynik LatentSync 1.6 (15:17–15:21):** klip bazowy EchoMimic 10 s (624×416) + `t02_10s.wav` → `ls_baza_10s.mp4`; inferencja 16 iteracji × 15 s = 4 min + restore 252 twarzy
(stage2_512, 25 kroków, bez deepcache) → **~25 s GPU na 1 s wideo**, nie „2×”, jak zakładałem; z `--enable_deepcache` i 20 krokami ok. 2× szybciej. `www/media/avatar/latentsync_baza_10s.mp4`.

**Wniosek z researchu:** klasa „LivePortrait” (Ditto, JoyVASA, EchoMimic 1.3B) to sufit, który user odrzucił; wyżej są tylko modele dyfuzyjne
14B (Wan S2V, LongCat, HunyuanVideo‑Avatar) i płatne API. Architektura skalowalna: **klip bazowy z Wan S2V/Kling raz na postać + LatentSync per film**
(18 GB, ~2× czasu audio) albo pełny S2V per film tylko dla premium/intro.

## 6u. „Totally unacceptable” — koszt: pomiary i modele czasu rzeczywistego (2026-09-03, 15:30–)

User odrzucił koszt Wan S2V (9 min GPU na 4,8 s). Dwa kierunki naraz:

**A. Ile da się urwać w S2V (pomiar, GPU2, 480², 77 klatek):** przebieg A = LoRA Lightning `wan2.2_t2v_lightx2v_4steps_lora_v1.1_high_noise` (4 kroki, cfg 1),
B = 20 kroków cfg 6 (model już w pamięci po A), C = powtórka A (ciepła pamięć). Skrypt `/opt/s2v_koszt.sh`, runner `/opt/s2v_run.py … [lora|-] [cfg]`.
Wyniki: **A (Lightning 4 kroki, 480², model w pamięci): 30 s na 4,8 s wideo ≈ 6 s GPU na 1 s filmu** (pierwszy przebieg 555 s zawierał ładowanie 14B i 20 kroków w 640²).
Jakość A: mimika i ruch jak w 20 krokach, lekko miększy obraz (`www/media/avatar/s2v_lightning_480_5s.mp4`, klatki `s2v_lightning_klatki.png`).
**B (20 kroków, 480², ciepła pamięć): 240 s** (≈ 50 s GPU na 1 s), **C (Lightning, powtórka): 30 s** — stabilne. Wniosek: koszt S2V bez LoRA to kroki, nie ładowanie;
Lightning daje 8× i jest akceptowalny wizualnie w rogu planszy. 4‑min film w Lightning ≈ 24 min GPU na jednej karcie (bez łańcucha chunków i bez optymalizacji SageAttention/torch.compile).

**B. Modele czasu rzeczywistego (research):**
- **SoulX-FlashHead** (Soul AI Lab, 12.02.2026, 1,3B, kod+wagi na HF `Soul-AILab/SoulX-FlashHead-1_3B`): „infinite real-time streaming talking heads”, oparty na Wan/LTX/self-forcing;
  **Lite: 96 kl./s na RTX 4090** (≈4× szybciej niż czas rzeczywisty, ≥ 8 GB VRAM), **Pro: 10,8 kl./s na 4090** (≥ 24 GB; na RTX 6000 Ada szacunkowo 14–16 kl./s ≈ 1,7 s GPU na 1 s wideo).
  Jest węzeł ComfyUI (`HM-RunningHub/ComfyUI_RH_FlashHead`). Licencja: README bez wpisu (wyszukiwarka: Apache‑2.0) — sprawdzić plik LICENSE. Instalacja `gpu_server:/opt/flashhead` w toku
  (uv py3.10, torch 2.7.1 cu128, flash-attn 2.8.0.post2 z gotowego koła, wav2vec2-base-960h).
- **LiveAvatar** (Alibaba‑Quark, 12.2025, 14B): 45 kl./s na wielu H800, single‑GPU wymaga 80 GB → nie na nasze karty.
- **MuseTalk 1.5** (MIT, modele „any purpose”): lip‑sync na gotowym wideo w czasie rzeczywistym (30 kl./s na V100, twarz 256²) — per film na klipie bazowym;
  instalacja `gpu_server:/opt/musetalk` w toku (mmcv/mmpose/mmdet 1.x dla DWPose).
- LatentSync 1.6: 25 s GPU/s wideo → za wolny per film (zmierzone 15:21).

**Wyniki SoulX‑FlashHead (15:37–15:45, portret foto + t02 40 s, 512², 25 kl./s):**
- **Lite (GPU1): 149 s** na 40 s audio łącznie z ładowaniem modelu ≈ 3,7 s GPU/s brutto; **Pro (GPU2): 293 s** ≈ 7,3 s/s brutto; log: denoise 0,4 s/krok, dekodowanie chunku 1,1 s.
  Deklaracje autorów (96 / 10,8 kl./s na 4090) są dla ciepłego procesu z SageAttention; u nas zimny start, bez SageAttention, `--audio_encode_mode stream` — jest zapas 2–5×.
- Jakość Pro: naturalna mimika (uśmiech, mrugnięcia, przechylenia głowy), spójna tożsamość, ciągłość klatek — blisko Wan S2V; Lite minimalnie mniej ekspresyjny.
  Klipy: `www/media/avatar/flashhead_{pro,lite}_28-40s.mp4` (kompozyt), `flashhead_{pro,lite}_t02.mp4` (pełne 40 s), klatki `flashhead_klatki.png`.
- Pułapki instalacji: `requirements.txt` z pinami (xformers, nvidia-nccl, transformers 4.57) nierozwiązywalne → bez pinów; ale **transformers musi być 4.57** (5.x zwraca `hidden_states=None`
  w wav2vec2 → `TypeError` w `preprocess_audio`), a do niego `huggingface_hub 0.36` + `diffusers 0.35` (0.40 wymaga nowszego hub). Zestaw działający: torch 2.7.1 cu128, flash-attn 2.8.0.post2 (koło), tf 4.57.3, hub 0.36.2, diffusers 0.35.2.
- MuseTalk: wagi komplet, ale `mmpose/mmcv-full` dla DWPose nie zainstalowane pod torch 2.7 (build ops) — odłożone, FlashHead jest lepszą ścieżką per film.

**Docelowa architektura pod tysiące filmów (do potwierdzenia liczbami):** FlashHead Pro/Lite per film (≈1–2 s GPU na 1 s filmu, 3 karty → ~50–100 filmów po 4 min na godzinę)
albo klip bazowy S2V raz na postać + MuseTalk per film (≈ czas rzeczywisty).

## 6v. Nowy koncept usera: POSTAĆ‑SYMBOL („karty” emocji zamiast gadającej buzi) (2026-09-03, 16:25–)

User (po odrzuceniu talking-head): jak na kanale Hoser / „Mia z Hyperlingua” — jedna postać (anime), kilkanaście gotowych stilli z emocjami i pozami
z JEDNEGO arkusza gpt‑image‑2, przełączanych w rytm narracji; bez lip‑synca, świadomie „nie‑realna”, jak mim‑symbol; per film kilka kart specjalnych,
w których postać wchodzi w interakcję z treścią planszy (trzyma strączek grochu, wskazuje tabelę). Tanie, deterministyczne, w duchu fabryki.

**Zbudowane (prototyp, 0 GPU, 0 LLM w renderze):**
- Arkusz 4×4 = 16 kart jednej postaci (`fabryka/avatar/karty/mia_v1_arkusz.png`, prompt `…prompt.md`; gpt‑image‑2 z `-i` portretem jako wzorcem tożsamości,
  zieleń #00FF00 w tle i rynnach, bez etykiet). Wyszło spójnie: neutral, uśmiech, wyjaśnia (otwarta dłoń), wskazuje, wskazuje w górę, myśli, zdziwiona, ciekawa,
  kciuk, wątpi, entuzjazm, macha, wzrusza ramionami, kiwa (oczy zamknięte), notes, świętuje. 1254² → karty ~300 px (do podniesienia: arkusz 1536² / 3×3 / upscale w ComfyUI).
- `fabryka/avatar/arkusz_siatka.py` — cięcie siatki R×C (rynny zielone/alfa, klucz zieleni jak w `arkusz_na_sprity.py`, wcięcie 1,5 % na linie rynien) → `karty/<zestaw>/<nazwa>.png` + `karty.json`.
- `14_avatar.py --tryb karty --zestaw mia_v1 [--specjalne 3:groch,7:wskazuje_lewo] [--wys2d 42cqh]`: granice zdań = najdłuższe pauzy obwiedni energii lektora
  (liczba = zdania z SCRIPT.md − 1; kropki są dłuższe niż przecinki), emocja zdania z heurystyki (pytanie → myśli, wykrzyknik → entuzjazm, „uwaga/ważne” → wskazuje w górę,
  liczby → wskazuje, „ale/jednak/problem” → wątpi, „zobacz/spójrz” → wskazuje, „brawo/dokładnie” → kciuk; reszta rotacja neutral/wyjaśnia/uśmiech/ciekawa), min. 2,5 s na kartę;
  pisze `assets/avatar/karty_cues.json` + kopiuje karty; `avatar.mjs` (tryb karty) składa overlay z N `<img>` i op `usta` (już istniejący crossfade) z `xf 0,3` i podskokiem 4 px
  na zmianie + `oddech` 0,6. Docelowo emocje z PLANU (mały model raz na film, jak REŻYSER), nie z heurystyki.
- **Kierunek od usera (16:40): postać Z CHARAKTEREM.** Nie wygląd, lecz osobowość „à la Sailor Moon”: własny głos TTS (inny niż lektor), który komentuje
  i narzeka, że „to wszystko zbyt skomplikowane” — humorystyczna odnoga eksperymentu; do tego „recepty MrBeasta” na utrzymanie uwagi (hak w pierwszych sekundach,
  stawka, przerywniki co ~30 s, w których postać reaguje). W tym systemie to jest tanie: REŻYSER dopisuje do PLANU `wtrącenia` (krótkie kwestie postaci
  z tagiem emocji i miejscem między zdaniami lektora), `10_polski_glos.py` nagrywa je drugim głosem, a warstwa kart pokazuje pasującą kartę (wzrusza/wątpi/zdziwiona)
  dokładnie na wtrąceniu. Do zrobienia po akceptacji prototypu kart.
**Wyniki (16:32–16:39):** scena `przebiegi/_ab-avatar/t02-karty`, render 2,5 min, check CZYSTY. v1: heurystyka dała „wątpi” (skrzyżowane ręce) na 45 s
(słowo „problem”, „tyle że”) — wyglądało ponuro → v2: przyjazna rotacja, „wątpi” tylko na zdaniach zaczynających się od „ale/jednak”, powitanie „macha” na
starcie, wypełniacze na przecinku po 7 s w długich zdaniach; karty specjalne na zdaniach 2/3/5 (pytanie, groch, wskazuje_lewo). 35 zmian kart na 266 s.
Klipy: `www/media/avatar/karty_v2_0-40s.mp4` (v1 obok), klatki `karty_v2_klatki.png`, arkusz i karty: `karty_mia_v1_arkusz.jpg`, `karty_mia_v1_podglad.png`,
`karty_specjalne_v2.jpg`; www §2l. Do poprawy: rozdzielczość kart z arkusza 4×4 (~300 px; arkusz 1536² lub 3×3 albo upscale w ComfyUI), wyrównanie skali
kart specjalnych do kart z arkusza (inny kadr), emocje z PLANU zamiast heurystyki.
**Poprawka obrazków (16:45–16:55, „obrazki są jeszcze do poprawki”):** (1) rozdzielczość: karty na zieleni → `pct push` do CT124 → ComfyUI
`UpscaleModelLoader(4x-AnimeSharp) → ImageUpscaleWithModel` przez API (`/opt/karty/upscale.py`, 19 kart w 20 s) → `pct pull` → ponowny klucz →
zestaw `fabryka/avatar/karty/mia_v1_up` (~1000×1140 px); (2) klucz zieleni ostrzejszy (rampa 70–160, erozja 1 px, despill) — bez zielonej obwódki;
(3) w toku: arkusz w czystym stylu anime 3×3 (większe karty) do wyboru stylu oraz karty specjalne jako JEDEN arkusz 2×2 (ten sam kadr co arkusz główny);
obie generacje padły dwa razy na `404 chatgpt.com/backend-api/codex/responses` (codex2) → ponowione na codex1. Render v3 (`karty_v3_0-40s.mp4`) CZYSTY.
**Doprecyzowanie usera (16:58): „nie chodzi o upscale, tylko o zbyt wielkie gestykulacje — bardziej moderate (nie przesadź w drugą stronę)”.**
Dwa ruchy: (1) od ręki — `--bez entuzjazm,swietuje,zdziwiona` (karty teatralne poza automatem), rotacja spokojniejsza (neutral/uśmiech/wyjaśnia/ciekawa/kiwa),
render v4 z istniejących kart; (2) nowy arkusz 4×4 „umiar” tej samej postaci (prompt `arkusz_umiar.md`: małe gesty przy ciele, bez uniesionych rąk, bez otwartych
szeroko ust, spokojne brwi; wzorzec tożsamości = arkusz v1). Backend Codex (`chatgpt.com/backend-api/codex/responses`) zwracał 404 na obu kontach
(codex1/codex2) od ~16:45 — pętla ponawiania co 3 min (`scratchpad/karty/retry_umiar.sh`), po sukcesie: cięcie → upscale → render v5.
Render v4 (`karty_v4_0-40s.mp4`, 16:59, CZYSTY): macha → neutral → pytanie → groch → wyjaśnia → kiwa → wskazuje → wskazuje_lewo → uśmiech → kciuk…
**User (17:15): „wyciągnięta ręka albo machanie zupełnie nie pasują… ty chyba nie masz smaku, daj to Codexowi, żeby 16 pozycji napisał”.** Zrobione (17:20–17:35):
- brief PL (`scratchpad/codex_karty/brief.md`) → `codex1 exec -m gpt-5.6-sol -c model_reasoning_effort="high" --image=mia_v1_arkusz.png --output-schema` → JSON
  `fabryka/avatar/karty/mia_v2_pozycje.json` (zasada, opis postaci, 16 × {nazwa, kiedy, opis_en}, `prompt_arkusz_en`) + `mia_v2_arkusz.prompt.md`;
  pozycje: sluch_neutral, sluch_lewo, sluch_cieplo, ciekawosc / namysl, aha, lekkie_zdziwienie, zgoda / sceptyk, dumny_wynik, pokaz_lewo, skup_notuje /
  to_wazne, skomplikowane, maly_sukces, koniec (5 kart patrzy w lewo na planszę; gesty przy tułowiu, bez wskazywania i machania);
- backend obrazów Codexa wrócił ~17:21 (pętla „umiar” też się udała — ale mój arkusz nadal miał machanie/kciuk/wzruszenie → odrzucony, `karty_umiar_arkusz.jpg`);
- arkusz `mia_v2_arkusz.png` (gpt‑image‑2, `-i` arkusz v1) → `arkusz_siatka.py` → `mia_v2/` (230×290) → zielone wycinki → CT124 AnimeSharp ×4 (16 kart, 10 s;
  wejście `/home/comfy/ComfyUI/input/karty_<n>.png`, wyjście `output/karty_up/`) → klucz → `mia_v2_up/` (924×1160) + `groch` skopiowany z v1 (karta specjalna Mendla);
- `14_avatar.py`: dobór kart przez ROLE (`ROLE` + `karta_dla()`), heurystyki zdań → rola, otwarcie = rola „start” (sluch_cieplo), ostatnie zdanie = „koniec”,
  `--specjalne nr:nazwa|rola`; stare nazwy mia_v1 jako zapas w każdej roli;
- render v5 (`--zestaw mia_v2_up --specjalne 3:groch`): cues sluch_cieplo → sluch_neutral → ciekawosc → groch → sluch_neutral → zgoda → pokaz_lewo → zgoda → …
  (baza spokojna; karty „aha/namysl/lekkie_zdziwienie/to_wazne/skomplikowane/maly_sukces” wchodzą tylko z heurystyk/PLANU — w t02 rzadko; do rozważenia
  szersza rotacja albo emocje z REŻYSERA). Render v5 (17:31, CZYSTY, 35 zmian kart / 31 zdań) → `karty_v5_0-40s.mp4`, `karty_v5_klatki.png`, `karty_v5_zblizenie.png`.
- `--rotacja spokojna|szeroka` (ROTACJE w `14_avatar.py`): „szeroka” dokłada do obiegu namysl/aha/skup_notuje/lekkie_zdziwienie/dumny_wynik → render v5b
  (`karty_v5b_70-110s.mp4`; 0–40 s identyczne z v5, różnice od 76 s: aha → skup_notuje → ciekawosc → … → lekkie_zdziwienie). Użycie kart w v5b:
  sluch_neutral 8, sluch_cieplo 5, ciekawosc 4, pokaz_lewo 4, zgoda 3, aha 3, sluch_lewo 3, skup_notuje 2, groch/lekkie_zdziwienie/namysl 1;
  sceptyk, dumny_wynik, to_wazne, skomplikowane, maly_sukces, koniec — 0 (czekają na emocje z PLANU / heurystyki dopasowane do tekstu).
- Karty specjalne per film: gpt‑image‑2 z arkuszem jako `-i` + opis stroju/fryzury (pierwsza próba z samym portretem odpłynęła: trzy różne dziewczyny/style);
  potem `arkusz_siatka.py --wiersze 1 --kolumny 1` i dopisanie do zestawu; wplatanie na zdaniach `--specjalne nr:nazwa`.

## 6w. Zwrot koncepcyjny usera: nie avatar, tylko WTRĄCENIE (2026-09-03, 17:45–)

**User (17:40):** „popełniamy błąd, że identyfikujemy tę postać z avatarem — statycznie w jednym miejscu, 90 % czasu bez emocji. Ma się pokazywać tylko, gdy coś
ważnego się dzieje, z emocją, niekoniecznie w tym samym miejscu, w różnym powiększeniu, na tyle, ile trzeba; jak gotowy element HyperFrames, niekoniecznie
szanujący miejsce innych elementów: pokazuje się na chwilę i wskazuje element albo staje obok. Trzeba przemyśleć jej rolę w przekazie.” Moja diagnoza
(zaakceptowana): postać = **wtrącenie** (reaction shot / didaskalia), pełnomocnik widza; funkcje: wskaż, zareaguj, podkreśl, przejdź, skomentuj (głos);
pozycja względem celu, skala = natężenie, 2–6 s, wejście z przerysowaniem, limit 2–3 na tablicę. Nieobecność czyni pojawienie się miłym.

**Implementacja (17:50–18:05):**
- `fabryka/komponenty.mjs` RUNTIME: nowy op `wtracenie` (`sel, at, hold, wejscie: pop|wsuw|wychyl, tin, tout, dx, dy, rot, karty:[[t_rel, idx]], nudge`):
  pop = scale 0,55→1 `back.out(1.9)`; wsuw/wychyl = z (dx,dy,rot) do 0 i z powrotem (wychyl = `back.out(1.5)`, origin w rogu kadru); crossfade kart 0,25 s
  względem `at`; „dźgnięcie” w stronę celu; oddech (scaleY) w czasie trwania; wszystko tweeny/sety → seek-safe. Dodany do `OPY_DOZWOLONE` i `koniecOpu`.
- `fabryka/avatar/avatar.mjs`: tryb `assets/avatar/wtracenia.json` (pierwszeństwo przed `karty_cues.json`): element `.wtr` per wtrącenie
  (left/top w cqw/cqh, height w cqh, drop-shadow, `img.lustro` = odbicie), op `wtracenie` + opcjonalny `pulse` celu (s0 1,045).
- `14_avatar.py --tryb wtracenia --zestaw mia_v2_up,mia_v1_up [--plan PLIK]`: plan = `<scena>/WTRACENIA.json` (didaskalia: `at, hold, wejscie, cel ig:rN | krawedz prawa/lewa,
  strona prawa/lewa/dol/gora, wys, karty [[t, nazwa, lustro?]], puls, nudge`); geometria: ramka `#pl-ig` z plansza.mjs + `assets/ig/01.regiony.json` (bbox_pct)
  → pozycja w % kadru (przy celu, dół wyrównany do dołu celu; wychylenie = 70 % widoczne zza krawędzi); karty z kilku zestawów (odrzucone wcześniej
  `wskazuje`/`wskazuje_lewo` wracają — teraz wskazują REALNY element); kasuje `karty_cues.json`.
- Plan t02 (6 wtrąceń, obecność 30 s / 266 s = 11 %): 0,6 s powitanie zza prawej krawędzi (sluch_cieplo) · 23,0 s sceptyk przy „?” (prawa strona r2) ·
  67,5 s „za skomplikowane” przy samopylności (lewa strona r3; sluch_lewo→skomplikowane w lustrze) · 100,9 s „to ważne” przy definicji linii czystej
  (wsuw z prawej, wskazuje_lewo→to_wazne, puls r4) · 225,3 s wskazanie „osiem roślin” (lewa strona r6, wskazuje→pytanie, puls r6) · 261,0 s pożegnanie
  zza krawędzi (koniec). Czasy: zdania z `audio_meta.json` (słowa) + `fadeIn` regionów z OPS.
- **Wynik (18:12–18:15):** dwa rendery CZYSTE (v1 i poprawka: w wt4 `wskazuje_lewo` miała inny kadr niż reszta kart → skok przy przenikaniu; zamienione
  na `wskazuje` w lustrze). Media: `www/media/avatar/karty_wt1_montaz.mp4` (6 segmentów po ~7 s), `karty_wt1_klatki.png`; www §2m. Zbliżenia 1:1 wyglądają
  dobrze: sceptyk obok „?”, palec dokładnie na etykiecie „osiem roślin”, „to ważne” na prawo od regionu linii czystych.
- **Co dalej (propozycje):** (1) karty projektowane POD wtrącenia, w jednym kadrze: wskazujące w obie strony, zbliżenia twarzy (reakcje) w wyższej
  rozdzielczości, wychylenia zza krawędzi (sama głowa + dłoń), cała sylwetka na otwarcie/zamknięcie — brief do Codexa jak przy mia_v2; (2) didaskalia
  z REŻYSERA w PLANIE tablicy (`wtracenia: [{funkcja, cel, emocja, kwestia?}]`, limit 2–3, ≥10 s odstępu) zamiast ręcznego `WTRACENIA.json`;
  (3) funkcja „skomentuj” z drugim głosem TTS (postać marudząca: „no i po co to komu?”) w pauzach lektora; (4) wychylenie zza krawędzi karty
  infografiki (a nie kadru) — wymaga warstwy pod `#pl-ig` albo clip-path; (5) reguła anty-Clippy w walidatorze: max 3 wtrącenia / tablicę, brak nakładania na cel.

**User (18:20): „lepiej, ale tranzycje niekoniecznie dobre — lepiej pokazać postać nagle w danym miejscu, nie animować znikąd ani z boku slajdu.
Zrób całość, wtedy ocenię. Pytanie: co ocenia, gdzie ma się pojawić avatar? Czy to nie nazbyt tokenożerne? Sprawdź i zobaczymy na pełnym odcinku.”**
- Wejście `ciecie` (domyślne): `tl.set opacity 1` w `at`, `0` w `at+hold`; bez pop/wsuw/wychyl (te zostają jako opcje planu).
- **Planer deterministyczny `--tryb wtracenia --auto` (0 tokenów)** — `plan_auto()` w `14_avatar.py`: wyzwalacze ze struktury, którą fabryka JUŻ ma:
  odsłonięcie regionu (`fadeIn/pop #pl-ig-rN` w OPS zbudowanej klatki) → „wskaż” (score 3), pulse regionu → „wskaż” (2,2), zdanie-pytanie → `pytanie` (2),
  słowa-klucze (ważne/definicja/warunek/zasada…) → `to_wazne` (2,4), zawiłość → `skomplikowane` (1,8), zaskoczenie → `lekkie_zdziwienie` (1,6),
  kontra (ale/jednak/wcale) → `sceptyk` (1,4). Budżet: `maks = clamp(dur/55, 1, 4)`, odstęp ≥ 12 s, najwyżej połowa budżetu na „wskaż”,
  powitanie na pierwszej i pożegnanie na ostatniej tablicy (`--pierwsza/--ostatnia`). Pozycja: strona celu z wolnym miejscem (lewa/prawa/dół; kolizje
  z widocznymi w tej chwili regionami liczone polem), inaczej margines poza kartą infografiki. Karty: `wskazuje`(lustro wg strony)→`sluch_lewo`,
  `to_wazne`, `pytanie`, `skomplikowane`, `lekkie_zdziwienie`, `sceptyk`, `sluch_cieplo`, `koniec`.
- **Rachunek tokenów:** wariant deterministyczny = 0. Wariant LLM (model pisze didaskalia z listy zdań + regionów) — szacunek promptu z planera:
  t01 882 · t02 1542 · t03 1297 · t04 803 · t05 760 · t06 742 · t07 737 = **~6,8 k tokenów wejścia + ~7×250 wyjścia ≈ 8,5 k tokenów na film** (7 tablic),
  czyli ~0,5 % kosztu produkcji filmu w trybie warstwy (~1,8 M tokenów, §7). Wniosek: LLM nie jest tokenożerny, ale jest ZBĘDNY do wyboru miejsc —
  wyzwalacze są w strukturze; LLM ma sens tylko dla funkcji „skomentuj” (kwestie z charakterem) i ewentualnie do odsiewu (które odsłonięcia zasługują).
- **Pełny odcinek:** kopia przebiegu `przebiegi/20260903-mendel-wtracenia/` (z `20260902-mendel-ig7`, 7 tablic PMENDb, 668 s), plan auto na każdej
  (t01 2 · t02 4 · t03 3 · t04 1 · t05 1 · t06 1 · t07 2 = 14 wtrąceń, obecność ~65 s ≈ 10 %), render harness ×7 (po 3 naraz), sklejka `FILM.mp4`
  (`scratchpad/karty/sklej_film.py`: concat -c copy + 720p do wysyłki `www/media/avatar/FILM_wtracenia_720p.mp4` + arkusz kontrolny `karty_wt_film_klatki.png`).
- **Pierwszy przebieg (18:55–19:01, 7×rc 0, FILM 43 MB / 668 s):** arkusz kontrolny wykazał błąd planera: przy remisie kolizji strona „dol” wygrywała
  z „lewa/prawa” alfabetycznie → postać stała pod kartą i wskazywała w bok (nonsens). Poprawka: bok przed dołem, a gdy naprawdę pod celem — `wskazuje_gore`
  (palec w górę) → `ciekawosc`; ponowne plany (wszystkie 14 po bokach celów) i drugi render 7 tablic. Uwaga na przyszłość: margines prawy koliduje z blokami
  wspierającymi (t07: „WERDYKT”) — planer nie zna ich prostokątów (są dokładane przez frameShell); do zrobienia: eksport rectów bloków do raportu harnessa.

## 6x. Poprawka po ocenie pełnego odcinka (2026-09-03, 19:15–)

**User (19:10):** „z jednej strony dobrze, z drugiej trochę rozprasza. FAIL: wysłałeś starą wersję prezentacji (slajdy sprzed poprawek, tytuły po angielsku);
avatary są takie same — musi być różnorodność w obrębie lekcji (kilka–kilkanaście obrazków gpt-image tym samym promptem); głos męski, a avatar żeński — zdecyduj się.”
- **Błąd:** za bazę wziąłem `20260902-mendel-ig7` (stary bieg, kickery EN, lektor am_michael). Bieżąca produkcja to `20260902-mendel-ig9-avatar`
  (= ig8: kickery PL, t05 poprawione, `fadeIn` zamiast `pop`) **z lektorką f06** (`10_polski_glos.py --glos f06`, §6i) — `audio_engine_meta.json` tam kłamie
  (nadal „am_michael”), prawda jest w `assets/voice/01.wav` i `audio_meta.json` (t01 87,3 s vs 73,0 s).
- **Decyzja głos/postać:** zostaje dziewczyna (koncept usera) + lektorka f06 → nowa kopia `przebiegi/20260903-mendel-wtracenia2/` z ig9-avatar,
  stary sprite-avatar zdjęty z 7 tablic (`14_avatar.py --off`, `assets/avatar` skasowane).
- **Różnorodność rysunku:** 6 dodatkowych arkuszy TYM SAMYM promptem (4× prompt Codexa mia_v2 z `-i mia_v2_arkusz.png`, 2× prompt mia_v1 dla kart
  wskazujących; codex1/codex2 równolegle, `scratchpad/karty/warianty/gen.sh`) → `warianty_pipeline.py` (cięcie → zielone wycinki → CT124 ×4 → klucz) →
  zestawy `mia_v2b_up … mia_v2e_up`, `mia_v1b_up`, `mia_v1c_up`. Instalator: ta sama nazwa karty w kilku zestawach = warianty; wtrącenie n bierze wariant
  `n % K` (licznik ciągły przez tablice filmu: `--wariant0`), obie karty wtrącenia z tego samego arkusza. Pliki w scenie: `<zestaw>__<karta>.png`.
- **Mniej rozpraszania:** hold „wskaż” 5 → 4,2 s, wysokość 30 → 27 cqh, po wskazaniu bez drugiej karty (za krótko na przenikanie).
- **Dryf tożsamości w wariantach v1 (19:40):** arkusze `v1b/v1c` (prompt mia_v1 bez opisu postaci, mimo `-i`) dały INNĄ dziewczynę (anime, biała bluzka,
  jaśniejsze włosy) → odrzucone (`mia_v1b_up`, `mia_v1c_up` zostają na dysku jako dowód; podgląd `karty_warianty_podglad.png`). Warianty v2 (prompt Codexa
  z opisem postaci) są spójne. Wskazywanie przeniesione na `pokaz_lewo` (otwarta dłoń, 5 wariantów; lustro wg strony) z palcem `wskazuje` (v1) co 4. raz.
  Wniosek na przyszłość: każdy prompt arkusza MUSI zaczynać się opisem tożsamości (jak u Codexa), sama referencja `-i` nie wystarcza.

## 6y. Lektor: tempo i pauzy (2026-09-03, 20:10–) + polskie znaki w infografikach

**User (20:05):** „lektor mówi cały czas wolno i są nienaturalne pauzy; pauzy tylko między slajdami, nie tak rozwlekle”. Wcześniej (§6h) pomiar
wykazał to samo na m04 i f06 (omnivoice na całym akapicie: 0,8–1,0 s po każdej kropce), a wtedy zapisałem „user nie chce cięcia i sklejania”.
- **Pomiar t05 (f06, 117 słów):** akapit = 63,8 s, 111 słów/min, 19 pauz ≥ 0,3 s, 9 > 0,8 s, cisza 20 %; po kropce mediana 0,80 s (ASR),
  po przecinku 0,26 s. Kokoro am_michael: 128–136 słów/min, cisza 17–18 %.
- **Próby na boxie CT111 (`/v1/audio/speech`, omnivoice):** A `speed 1.15` na akapicie → 57 s, pauzy zostają (max 0,97 s; speed skraca mowę,
  nie pauzy); B/C zdaniami (osobna synteza każdego zdania, sklejka 0,35 s) → pauzy nadal 0,5–0,9 s, bo omnivoice zostawia ciszę na brzegach
  fragmentu; **D = zdaniami + przycięcie ciszy na BRZEGACH (próg −42 dB, margines 60 ms) + stała przerwa 0,3 s + `speed 1.15` + 0,7 s ciszy na końcu
  tablicy (pauza „między slajdami”)** → 55,1 s, 127 słów/min, 0 pauz > 0,8 s (max = końcowe 0,7 s), cisza 14 %. To nie jest cięcie mowy —
  cięta jest tylko cisza na końcach fragmentów.
- Skrypt: `fabryka/biblioteka/16_glos_zdaniami.py --scena <scena> [--glos f06 --speed 1.15 --przerwa 0.3 --koniec 0.7]` (działa na scenie przebiegu:
  SCRIPT.md → 01.wav (stare → `01.f06_akapit.wav`), words z faster-whisper (venv `venv_asr`), audio_meta/film.json/index.html, `audio_engine_meta`
  z prawdziwym silnikiem). Zdania ≤ 2 słowa doklejane do poprzedniego. Próbki A/B dla usera: `scratchpad/glos/probka_{stara,D}_t05_*.m4a`.
- Zastosowane do 7 tablic `20260903-mendel-wtracenia2` (t01 87→78 s, t02 266→232 s, t03 207→179 s, t04 71→63 s, t05 64→56 s, t06 54→47 s, t07 53→47 s;
  film 803 → 701 s). **Błąd kolejności, wyłapany przed wysyłką:** planer czyta czasy odsłonięć regionów z OPS ZBUDOWANEJ klatki, a klatki były z poprzedniego
  renderu (stary lektor) → wtrącenia rozjechałyby się o 10–13 %. Naprawa: `HF_TYLKO_BUILD=1 node fabryka/pilot/harness.mjs <scena>` (nowy przełącznik:
  zapisuje klatkę i kończy bez renderu) przed planowaniem + strażnik w `plan_auto()` (data-duration klatki ≠ długość lektora → SystemExit z podpowiedzią).
  Kolejność kanoniczna: lektor → build → plan → render → sklejka.
- **Polskie znaki (user 19:50: „dlaczego slajdy nie mają polskich liter?”):** przyczyna w ZAMÓWIENIACH infografik agenta planszy
  (`infografiki.jsonl`): t01/t05/t06 mają 0 znaków diakrytycznych (nawet w opisie), t02/t03/t07 mają; pipeline nic nie zdejmuje, gpt-image-2 renderuje
  ogonki poprawnie (t03). Naprawa: poprawić 3 zamówienia → nowe obrazy → regiony/maski → render 3 tablic (ryzyko: inna liczba regionów niż opy
  w plansza.mjs); zabezpieczenie: walidator w masówce (polski tekst bez ani jednego znaku PL = odrzuć) + zdanie w prompcie. CZEKA NA „TAK” usera.

## 6z. Lektor z Gemini TTS (2026-09-03, 20:50–)

**User (20:48): „daj tu ttsa z gemini”.** Silnik `gemini` modułu :8772 = bramka `:8793` (`google_ai_studio/gateway/server.py`; była wyłączona → uruchomiona
nohup, log w scratchpad `glos/gateway.log`). Głos: id banku `f06` → preset źródłowy **Zephyr** (bank to klony presetów Gemini, więc barwa ta sama, tylko
prawdziwa prozodia zamiast klonu). `16_glos_zdaniami.py --silnik gemini [--style "…"] [--model-tts …]`: cały akapit jedną narracją (bramka tnie ~2800 zn.
i skleja), cisza przycięta na brzegach, 0,7 s na końcu tablicy; words z faster-whisper jak dotąd; `audio_engine_meta` → `gemini-bramka`.
- **Próba t05 (117 słów):** 50,5 s (omnivoice akapit 63,8 · omnivoice zdaniami 55,6), **141 słów/min**, między zdaniami pauzy < 0,3 s (jedyna ≥ 0,3 s to
  końcowa 0,7 s), synteza 24,6 s, koszt **0,0125 $** (`X-Cost-USD`; flash 2.5, domyślny model bramki). Styl: „Lektorka kursu edukacyjnego: naturalne, żwawe
  tempo, wyraźna dykcja, krótkie pauzy między zdaniami, bez patosu.” Próbka dla usera `scratchpad/glos/probka_G_t05_gemini_22s.m4a`.
- Zastosowane do 7 tablic (stare audio zdaniami → `01.f06_zdaniami.wav`), potem kanoniczna kolejność: build → plan wtrąceń → render → sklejka.
  Szacowany koszt filmu ~0,2 $ (700 s × 0,9 $/h). Limit klucza AI Studio: ~1000 req/dzień, niski RPM — masówka wielu filmów naraz wymaga semafora (bramka ma 4).
- **User (23:05): „głos zmienia się na każdym slajdzie — bug Gemini? mieliśmy referencyjny głos”.** Pomiar barwy per tablica (f0 mediana / centroid widmowy):
  Gemini Zephyr: f0 168–211 Hz (t01 = 211, reszta 168–185), centroid 1369–1732 Hz z trendem spadkowym t01→t07; klon omnivoice f06 (ta sama produkcja,
  `01.f06_*.wav`): f0 175–183, centroid 1709–1889 — stabilny. Przyczyna: Gemini gra PRESET (Zephyr) bez audio referencyjnego i bez `seed`
  (API.md bramki: „bez seed ~1/10 generacji różni się tempem/akcentem”), każde wywołanie = inne wykonanie; klon omnivoice (`ref_audio` z banku) jest
  spójny z definicji — to jest ten „referencyjny głos”. Nie bug, cecha silnika.
- **Bramka spójności barwy** w `16_glos_zdaniami.py` (gemini): `--takes N --ref-f0 177 --ref-centroid 1550 --tol-f0 0.06 --tol-cent 0.10` — do N podejść,
  pomiar `barwa()`, wybór najbliższego wzorca (odległość znormalizowana tolerancjami), odrzucone podejścia jako `01.gemini_takeK.f32`. Wzorzec = mediana
  7 tablic z pierwszego przebiegu. Zastosowana do odstających t01 i t07 (wyniki w www §2m). Alternatywa bez ryzyka: klon f06 zdaniami (§6y).

## 6aa. Infografika na tle planszy (2026-09-03, 21:10–): „dopasować do pikseli tła z HyperFrames”

**User:** „do poprawki background planszy z image — nie wiem, czy robimy dark theme czy white theme; trzeba dopasować do pikseli tła z HyperFrames.”
Diagnoza: infografiki idą w stylu `papier` (kremowe #F1EAD9 płótno) i siedzą jako kremowa karta na granatowej planszy (skin `repochad-dark`,
bg #0A0E1A, ground = gradienty). W `infografika.mjs` od dawna jest `--styl skin` (płótno w kolorze bg skina, tusz #F4F6FB, jeden akcent), nigdy nie użyty
w produkcji. Wycinacz `regiony.py` liczy tło jako medianę brzegu i tusz jako |rgb−tło|₁ > 60, więc działa na dowolnym płaskim kolorze.
**Rozwiązanie:** (1) prompt stylu skin wzmocniony (`prompt_szablon.py::_styl`, `infografika.mjs` twardy warunek): JEDEN płaski, jednolity kolor
płótna dokładnie bg skina, bez gradientu/winiety/tekstury, wszystko JAŚNIEJSZE od tła (bez ciemnych cieni i wypełnień) — żeby dało się kluczować;
(2) `fabryka/pilot/ig_na_tlo.py --scena <scena>`: klucz tła → alfa (rampa L1 14–44 od mediany brzegu; piksele ciemniejsze od tła = tło), zapis
`NN.flat.png` (na nim liczył regiony.py), podmiana w `plansza.mjs` (kopia `plansza.papier.mjs`): `#pl-ig-podklad{display:none}`, `aspect-ratio` z wymiarów,
`transform-origin` regionów i `data-region` z nowego `NN.regiony.json`. Efekt: rysunek leży bezpośrednio na gradiencie planszy (dopasowanie z definicji),
maski/warstwy bez zmian. (3) regeneracja 7 infografik tym samym zamówieniem (`scratchpad/ig/zamowienia.py`: sloty odtworzone z `01.prompt.md`,
etykiety/opisy t01/t05/t06 z polskimi znakami — rozwiązuje też §6y), stare `assets/ig` → `assets/ig_papier`.
- **Natywna przezroczystość gpt-image (user: „ostatnio wprowadzono w Codexie, nie wiadomo, jak z duchami pikselowymi”):** test `scratchpad/ig/alfa/`
  (prompt z „TRANSPARENT background, PNG with alpha”) — wynik i pomiar półprzezroczystych pikseli („duchy”) niżej.
- **Wynik testu alfy (22:28):** Codex image_gen ODDAJE prawdziwe RGBA na prośbę w prompcie: 1024×1536, alfa 0–254, 61,7 % pikseli w pełni
  przezroczystych, 34,1 % kryjących, **4,2 % półprzezroczystych** (krawędzie + miękkie cieniowanie), a pod nimi kolor OBIEKTU (śr. RGB 166/186/154),
  nie czerń/biel → kompozyt na ciemnym i jasnym tle czysty, bez obwódek (`www/media/avatar/ig_alfa_test.png`, zbliżenie 1:1 w scratchpad).
  Wniosek: natywna alfa nadaje się na warstwę wyświetlaną; wycinacz dalej liczy na SPŁASZCZONYM obrazie (narzędzie i tak spłaszcza alfę na kolor
  stylu i zostawia `NN.rgba.png`) — `ig_na_tlo.py` może brać `NN.rgba.png` zamiast kluczować. W tej produkcji użyto kluczowania płaskiego tła skina.
- **Pułapka numeracji:** `infografika.mjs` nie nadpisuje istniejącego `01.png`, tylko numeruje dalej (`02.png` + `02.*`), a plansza zna tylko `01.*` →
  pierwszy pilot kluczował STARY kremowy obraz (rysunek papierowy z wyciętym kremem na granacie = „duchy” z ciemnym tuszem). Driver `zamowienia.py`
  przenosi teraz `NN.*` → `01.*` (stare w `assets/ig_papier`). Pilot t01/t05 po poprawce: obraz leży na gradiencie bez widocznego prostokąta,
  nagłówek w akcencie skina, etykiety z polskimi znakami (`www/media/avatar/ig_skin_pilot.png`). Tło modelu wyszło (0,4,19) zamiast #0A0E1A —
  klucz bierze medianę brzegu, więc bez znaczenia.

## 6ab. WNIOSKI DNIA 2026-09-03 (konsolidacja; szczegóły w §6v–§6aa, kanon: `external_connections/{avatar,tts,image_generators}`)

**Postać.**
1. Postać-symbol to **wtrącenie, nie avatar**: pojawia się tylko, gdy ma funkcję (wskaż / zareaguj / podkreśl / przejdź / skomentuj), przy celu, na 3–5 s,
   na cięcie (bez animacji znikąd), 1–4 razy na tablicę, obecność ~8–10 % czasu. Statyczny avatar w rogu = 90 % czasu bez emocji = nuda.
2. **Gdzie i kiedy decyduje struktura, nie LLM**: odsłonięcia/pulsy regionów infografiki z OPS, zdania-pytania, słowa-klucze, kontra. Planer
   `14_avatar.py --tryb wtracenia --auto` = 0 tokenów. Wariant LLM kosztowałby ~8,5 k tokenów/film (0,5 % produkcji) — sensowny tylko dla kwestii z charakterem.
3. **Karty**: 16 pozycji zaprojektował Codex (gpt-5.6-sol, 17,3 k wejścia + 2,8 k wyjścia, raz); gesty umiarkowane przy tułowiu, ≥ 5 kart patrzy na planszę.
   Karty wskazujące są dobre TYLKO gdy wskazują realny element (w avatarze raziły, we wtrąceniu działają).
4. **Różnorodność**: kilka arkuszy tym samym promptem = warianty rysunku; wtrącenie n bierze wariant n mod K, licznik ciągły przez film.
   **Prompt arkusza MUSI zaczynać się opisem tożsamości** — sama referencja `-i` nie trzyma postaci (warianty v1 odpłynęły do innej dziewczyny).
5. Kolejność kanoniczna: **lektor → build (HF_TYLKO_BUILD=1) → plan wtrąceń → render → sklejka**; planer odmawia pracy na klatce z innym audio.

**Lektor.**
6. omnivoice na całym akapicie robi 0,8–1,0 s pauzy po każdej kropce (20 % ciszy, 111 słów/min); `speed` skraca mowę, nie pauzy. Rozwiązanie: zdanie po zdaniu,
   cisza przycięta na brzegach, przerwa 0,3 s, tempo 1,15, pauza 0,7 s tylko na końcu tablicy (`16_glos_zdaniami.py`) → 127 słów/min, 0 pauz > 0,8 s.
7. Gemini (preset Zephyr przez bramkę :8793) daje najlepszą prozodię (141 słów/min, 1,25 ¢/tablica), ale **barwa dryfuje między wywołaniami**
   (f0 168–211 Hz, centroid 1369–1974 Hz między podejściami tego samego tekstu) — brak audio referencyjnego i seeda. Klon omnivoice f06 jest spójny
   z definicji (f0 175–183). Bramka spójności (`--takes N`, wybór najbliższego wzorca) zmniejsza, nie usuwa. **Decyzja do podjęcia przez usera:**
   prozodia (Gemini + bramka) czy spójność (klon zdaniami).
8. `audio_engine_meta.json` bywał kłamliwy (am_michael przy f06) — prawda w `assets/voice/01.wav` + `audio_meta.json`; `16_glos_zdaniami.py` nadpisuje meta.

**Infografika.**
9. Kremowa karta na ciemnej planszy to błąd tematu. Styl `skin` (płótno w kolorze tła skina, jasny tusz, jeden akcent) + prompt o JEDNYM płaskim kolorze i
   zakazie ciemnych wypełnień + klucz tła do alfy (`ig_na_tlo.py`, podkład off) = rysunek natywnie na gradiencie planszy. Wycinacz działa bez zmian.
10. gpt-image przez Codex **oddaje prawdziwe RGBA na prośbę w prompcie** (4,2 % półprzezroczystych z kolorem obiektu, kompozyt czysty) — alternatywa dla klucza.
11. `infografika.mjs` nie nadpisuje `01.png` (numeruje dalej) — przy regeneracji przenieś `NN.*` → `01.*`, bo plansza zna tylko `01.*`.
12. Polskie znaki: agent planszy potrafi napisać zamówienie w ASCII (t01/t05/t06: 0 znaków PL); pipeline nic nie zdejmuje, model renderuje ogonki poprawnie.
    Potrzebny walidator zamówień (polski tekst bez ani jednego znaku PL = odrzuć) + zdanie w prompcie. NIEZROBIONE (tylko poprawka ręczna w zamówieniach).

**Proces.**
13. Wysyłaj to, co bieżące: bazą produkcji jest najnowszy bieg (ig9-avatar), nie pierwszy z brzegu (ig7) — kosztowało jedną rundę.
14. Arkusz kontrolny (klatka ze środka każdego wtrącenia) wyłapał dwa błędy przed wysyłką (dół zamiast boku; stary obraz po numeracji) — zostaje w pętli.

## 6ac. Lokalizacja gotowej produkcji bez ponownego liczenia plansz (2026-09-03, 23:20–) — test t05 → EN

**Pytanie usera:** czy da się przetłumaczyć film (TTS + „hooki językowe”) i ominąć ponowne planowanie plansz przez LLM?
**Mechanika (sprawdzona w kodzie):** plansza wiąże się z lektorem przez kotwice słów `K("słowo", ułamek)` → `kotwica()` w `komponenty.mjs`:
EXACT (znormalizowane słowo z ASR) → STEM (słowo lektora zaczyna się od kotwicy, ≥ 4 znaki) → FALLBACK (ułamek × długość sceny). Czas sceny = długość
audio. Elementy zależne od języka w 7 tablicach Mendla: 75 kotwic, 67 napisów HTML w `plansza.mjs` (52–253 znaków/tablicę), 33 etykiety wypalone
w infografikach, 1488 słów lektora; do tego słowa-klucze planera wtrąceń (polskie regexy) i fonty (Latin-Ext tak, cyrylica/CJK NIE).
**Test (t05, kopia w `przebiegi/_test-en/PMENDb-t05`):** jedno wywołanie Codex gpt-5.6-sol (JSON: narracja EN zdanie po zdaniu, mapa kotwic pl→en,
napisy planszy, etykiety obrazu) = 15,5 k wejścia + 1,3 k wyjścia (większość to narzut kontekstu Codexa, nie treść) → podmiana w SCRIPT.md
i `plansza.mjs` (11/11 kotwic, 12/15 napisów — 3 z myślnikiem/markupem wymagają dopasowania rozmytego) → infografika EN tym samym zamówieniem
(`--styl skin`, 0 tokenów, sędzia 0 ciężkich) → lektor EN (`16_glos_zdaniami.py --jezyk en`: bank ma refs `en`; Gemini EN działa, ale bramka
rzucała chwilowe 502 — skrypt uodporniony) → build → plan wtrąceń → render: **kotwice 11/11 EXACT, 0 fallback, `hyperframes check` czysty**,
choreografia i wtrącenia z polskiego wzorca.
**Pułapki:** (1) kotwica musi być słowem UNIKALNYM w tłumaczeniu — „takich”→„one” złapało pierwsze „One” w 0,05 s zamiast właściwego miejsca;
mapowanie musi sprawdzać unikalność i wybierać inne słowo z tego samego zdania; (2) długość napisów ±20 % (check łapie zasłanianie);
(3) etykiety w obrazach = regeneracja obrazów per język (deterministyczny prompt, ~80 s/obraz, 0 tokenów) — regiony/maski przeliczane;
(4) planer wtrąceń: słowa-klucze per język (odsłonięcia regionów i „?” są uniwersalne); (5) fonty tylko Latin — cyrylica/CJK wymagają innych plików;
(6) `audio_engine_meta`/`film.json` aktualizuje skrypt lektora. (7) Klatki testu pokazały dwa defekty ekstrakcji: kicker dzielony na spany
(`Policz, co się <b>różni</b>` → tylko „różni” przetłumaczone → „POLICZ, CO SIĘ DIFFER”) — tłumaczyć CAŁY blok z markupem inline, nie liście;
napisy z odstępem przed tagiem nie pasowały do wzorca `>tekst<` (3/15); etykiety slotów obrazu zostały polskie, bo mapa tłumaczeń była kluczowana
etykietami z pierwszego zamówienia (ASCII), a prompt w kopii miał już wersję z ogonkami — klucz mapy = etykieta znormalizowana (bez diakrytyków, lower). Szacunek na film: ~25–40 k tokenów tłumaczenia przy batchowaniu tablic (1–2 % produkcji),
~10 min generacji obrazów, TTS jak zwykle, render jak zwykle. Werdykt: TAK, przy lekkich modyfikacjach (skrypt `lokalizuj.py`: ekstrakcja → jedno
tłumaczenie → podmiana → obrazy → lektor → build → plan → render).
**Dwa tryby lokalizacji (user 23:50: „per język inny whisper timestamp — wielojęzyczne ścieżki na jednym filmie niemożliwe”):**
(A) *nowy render per język* — kotwice rozwiązują się na nowych słowach; osobne pliki wideo (to, co przetestowano). (B) *dubbing do polskiego mastera* —
polski timeline zostaje, obcy lektor zdanie po zdaniu wpasowany w slot polskiego zdania (`speed` per zdanie ± kilkanaście %, dopełnienie ciszą);
wtedy JEDEN obraz + N ścieżek audio (YouTube „multi-language audio”). Pomiar t05 (ten sam silnik omnivoice f06, speed 1,15, zdania 1:1 tym samym
splitterem, 8/8): EN/PL per zdanie **0,87–1,13**, łącznie 1,01, zdań poza ±15 % **0/8**. Wniosek: (B) jest wykonalne dla EN bez słyszalnego
przyspieszania; dla języków dłuższych (DE ~+15–20 %) potrzebny budżet długości w tłumaczeniu (zdanie ±10 % znaków) — to standard dubbingu.
Warunek konieczny: tłumaczenie jako tablica zdań 1:1 w schemacie odpowiedzi (wolny tekst dał 11 zdań wg ASR vs 8 wg splittera — segmentacja
interpunkcyjna ASR nie nadaje się do parowania; parować po zdaniach źródłowych).

## 6ad. Jakość renderu (2026-09-04, 00:20): harness renderował `--quality draft`

Pomiar t05 (50 s, 1080p, statyczna grafika), HyperFrames 0.8.27:

| tryb | profil H.264 | bitrate | rozmiar | czas renderu |
|---|---|---|---|---|
| draft (harness dotąd) | Constrained Baseline | ~300 kb/s | 2,5 MB | ~20 s |
| standard (domyślny HF) | High | 651 kb/s | 5,1 MB | 26 s |
| high | High | 891 kb/s | 6,5 MB | 26 s |
| high + `--resolution landscape-4k` | High, 3840×2160 | 2,2 Mb/s | 14 MB | 66 s |

Wniosek: draft jest OK w pętli agenta, ale masterem do publikacji ma być `high` (koszt czasu zerowy), a na YouTube warto wysyłać 4K
(YouTube daje wtedy lepszy kodek i bitrate także przy odtwarzaniu w 1080p; koszt 2,5× czasu renderu i 2× rozmiaru). Cały film 1080p high ≈ 80 MB,
4K ≈ 170 MB — hosting nadal groszowy. Harness: `HF_JAKOSC=high [HF_ROZDZIELCZOSC=landscape-4k] node fabryka/pilot/harness.mjs <scena>`
(domyślnie nadal draft, żeby nie spowalniać masówki). Porównanie 1:1 w scratchpad `jakosc/porownanie_1to1.png`.

## 7. Jak kontynuować

0. **Odcinek z postacią, infografiką na tle i lektorem (kolejność kanoniczna, 2026-09-03):**
   ```bash
   R=przebiegi/<RUN>; for t in PMENDb-t0{1..7}; do
     python3 fabryka/biblioteka/16_glos_zdaniami.py --scena $R/$t --silnik gemini --takes 3      # albo bez --silnik: klon f06 zdaniami (spójna barwa)
     HF_TYLKO_BUILD=1 node fabryka/pilot/harness.mjs $R/$t                                        # klatka z czasami odsłonięć
     python3 fabryka/biblioteka/14_avatar.py --scena $R/$t --on --tryb wtracenia --auto --wariant0 <licznik> --zestaw mia_v2_up,mia_v2b_up,mia_v2c_up,mia_v2d_up,mia_v2e_up,mia_v1_up
     node fabryka/pilot/harness.mjs $R/$t; done                                                   # (t01: --pierwsza, t07: --ostatnia)
   python3 <scratchpad>/karty/sklej_film.py $R www/media/avatar PMENDb                              # FILM.mp4 + 720p + arkusz kontrolny
   ```
   Infografika na tle planszy: `infografika.mjs … --styl skin --prompt szablon --regiony` → `NN.*`→`01.*` → `python3 fabryka/pilot/ig_na_tlo.py --scena $R/$t`.

1. Kolejny film w trybie warstwy (7 tablic, ~1,8M tokenów, ~15 min, 7 × $0,01):
   ```bash
   python3 fabryka/wzorcownia/06_masowka.py --run 2026MMDD-<nazwa> --zrodlo produkcje \
     --tablice <PID> --model "claude-opus-5[1m]" --tury 2 --wiedza pelna --wzorzec nie \
     --biblioteka --infografiki warstwy --workerow 7 --konta claude1
   python3 fabryka/biblioteka/11_porownanie.py --pid <PID> --tytul "…" --sklej <RUN>
   python3 fabryka/biblioteka/13_przeglad.py --pid <PID> --bieg "<RUN>=…"
   ```
   Nowy materiał: `06_produkcja.py --vid <VID> --pid <PID> --binarka codex1` (scenariusz
   w `fabryka/e2/scenariusz_<VID>_czasy.txt`), potem `10_polski_glos.py --pid <PID>` (m04).
2. Ocena obrazów bez renderu: `python3 przebiegi/_lab-wycinki/C/bieg.py zewn` + `tabela`
   (sędzia, 0 LLM); pojedynczy obraz: `lab_wycinki_metryki.py --png … --wyj … --tryb maski`.
3. Zmiana lektora: `10_polski_glos.py --pid <PID> --force` → `12_propaguj_audio.py --pid <PID>
   --run … --render` (sceny mają własne kopie audio).
4. Agenci labu: nowe agenty z `przebiegi/_lab-wycinki/BRIEF_WSPOLNY.md` + stanem katalogów;
   pauza = polecenie „zapisz stan i zakończ", NIE TaskStop (transkrypt przepada).
5. Kwoty: `aicheck` (claude1 reset wt 08.09, claude3 sob 05.09, claude2 pon 07.09).

## 8. Gdzie co leży

- Jedna kartka orientacyjna (składniki, stan, routing, co czytać): `MAPA.md`.
- Porównanie z wytwórnią (skalowalność, rekomendacja zbieżności, eksperymenty E1/E2): `POROWNANIE-WYTWORNIA-vs-FABRYKA-2026-09-02.md`.
- Repo: `README.md` (mapa), ten plik, `HANDOFF-2026-08-31.md` (dziennik z dowodami), `HANDOFF-AUDYT-2026-09-02.md`
  (przegląd całości dla audytora: etapy, liczby, werdykty, ryzyka, lista kontrolna).
- Kanon (`external_connections`): `NARZEDZIA.md` (wiersz gpt-image-2), `tts/README.md`
  (topologia CT111, głos m04), `image_generators/gpt_image_2/` (ZMIANY.md → README).
- Produkcje: `produkcje/PMENDb/`, `produkcje/PWYTB/` (szkielety z audio m04; `01.m01.wav`,
  `01.kokoro.wav` obok); przebiegi wg §2; poligony `przebiegi/_test-*` i `_lab-wycinki/`.
- Plan infografik (zatwierdzony przez usera, z tabelą walidacji): `~/.claude/plans/immutable-pondering-metcalfe.md`.
- Scratchpad (ulotny!): venv faster-whisper `…/scratchpad/venv_asr`, briefy codex1
  (`ig_brief.txt`, `ig3_brief.txt`, `ig4_brief.txt`) — treść bloków jest w `06_masowka.py`.
