# Plan integracji: tytuły, opisy i zdjęcia pod kanały sprzedażowe ## Cel i granice dokumentu Ten plik opisuje **copy compliance** (teksty i reguły kanałów) w obrębie tego repozytorium: przepływ **draft → AI → edytor → zapis do BaseLinker**. Nie zastępuje modelu merytorycznego importu ani mapowań ID. - **Źródło prawdy o architekturze JTL → BaseLinker:** [`ARCHITECTURE.md`](ARCHITECTURE.md). - **Stan wdrożenia, ID magazynów, kategorie, Render:** [`PROGRESS.md`](PROGRESS.md). - **Deploy:** [`DEPLOY.md`](DEPLOY.md). Zmiany treści tego planu należy **zsynchronizować** z akapitem „WDROŻENIE W TOKU — marketplace copy” w [`docs/NEW-CHAT-PROMPT.md`](NEW-CHAT-PROMPT.md) (skrót + odsyłacz do sekcji poniżej), żeby prompt startowy nie rozjeżdżał się z roadmapą. --- ## Inwentaryzacja kodu (punkt odniesienia) | Obszar | Plik(y) | Co robi dziś | |--------|---------|----------------| | Limity tytułu per kanał | [`config/editor-config.json`](../config/editor-config.json) (`channels.*.titleMaxChars`, `titlePrompt`, `bodyPrompt`) | Konfiguracja sufitów znaków i instrukcji dla modelu. | | Clamp, banlista i **polityka tytułu Allegro** po parsowaniu AI | [`src/openai.js`](../src/openai.js) — `finalizeChannelsFromParsed`, `clampChannelTitle`, `enforceBanlist` + [`src/channelTitlePolicy.js`](../src/channelTitlePolicy.js) | Skrócenie tytułu; banlista; dla **allegro** — osobne ostrzeżenia zgodności (długość, frazy, URL itd.). | | Polityka tytułu (moduł) | [`src/channelTitlePolicy.js`](../src/channelTitlePolicy.js) | `evaluateAllegroTitle` — reguły Allegro oddzielone od `banlistPhrases`; używane w `validateDraft`, `finalizeChannelsFromParsed`, UI. | | Testy kanałów / polityki Allegro | [`test/channel-titles.test.js`](../test/channel-titles.test.js), [`test/allegro-title-policy.test.js`](../test/allegro-title-policy.test.js) | Regresja clampów, `finalizeChannelsFromParsed`, reguły `evaluateAllegroTitle`. | | Lista zakazanych fraz (globalna) | [`config/editor-config.json`](../config/editor-config.json) — `banlistPhrases` | Marka źródłowa, clickbait — stosowana w promptach i po stronie `finalizeChannelsFromParsed`. | | Persony / ton | [`config/editor-config.json`](../config/editor-config.json) — `personas`, [`src/persona.js`](../src/persona.js) | `avoid` i kąt narracji — osobna warstwa od regulaminu Allegro. | | Szkielet opisu Base (7 sekcji) | [`config/editor-config.json`](../config/editor-config.json) — `salesFramework.sections` | Kontrakt kolejności bloków pod Base.com — **już istnieje**; nie zastępować drugim „silnikiem szablonów”. | | Snapshoty konfiguracji edytora | [`src/aiConfigSnapshots.js`](../src/aiConfigSnapshots.js) | Zapis / odczyt całego `editorConfig` — nośnik wersji, nie osobny silnik treści. | | Tytuł „twardy” / SEO | [`src/titleGenerator.js`](../src/titleGenerator.js) | Limit 75 znaków, wariant OLX, fakty z hintów strategicznych. | | Normalizacja draftu | [`src/draft.js`](../src/draft.js) — `normalizeDraftForSave`, `validateDraft` | Walidacja serwerowa + **`channelTitlePolicy`** dla włączonego Allegro → `meta.validationIssues` (`field`: `channelTitle:allegro`). | | Legacy edytor | [`public/editor.js`](../public/editor.js) — `validateDraftClient`, `renderValidation`, karty kanałów | Globalna skrzynka walidacji + **komunikaty przy polu** tytułu Allegro (`/deps/channelTitlePolicy.js`). | | Kreator (wizard) | [`public/wizard.js`](../public/wizard.js) — krok 5 „Podgląd” | Import tego samego modułu policy — ostrzeżenia pod skrótem kanału Allegro. | | Serwer | [`server.js`](../server.js) | Budowa draftu, **`GET /deps/channelTitlePolicy.js`** (serwuje `src/channelTitlePolicy.js` dla przeglądarki). | --- ## Luka vs roadmapa | Element z roadmapy | Status w repo | |---------------------|----------------| | Wielokanałowość + `titleMaxChars` + clamp | **Jest** | | Globalna `banlistPhrases` + czyszczenie w `finalizeChannelsFromParsed` | **Jest** | | **Walidacja wyłącznie pod tytuł Allegro** (np. 12–75 znaków, min. liczba wyrazów, lista zgodna z aktualną pomocą Allegro) | **Jest** — [`src/channelTitlePolicy.js`](../src/channelTitlePolicy.js) | | Jedna modułowa polityka (`channelTitlePolicy`), używana przy generacji **i** przy zapisie / edycji | **Jest** | | Komunikaty **przy polu** tytułu Allegro (nie tylko skrzynka walidacji) | **Jest** — legacy [`editor.js`](../public/editor.js); skrót w kreatorze [`wizard.js`](../public/wizard.js) krok 5 | | Szablony opisu **per kanał** jako osobna warstwa | **Częściowo** — są `bodyPrompt` / `salesFramework`; Faza B = **różnicowanie**, nie przebudowa od zera | | Checklista zdjęć + link do help Allegro w UI | **Jest** — Faza B (moduł UI + link do pomocy Allegro; patrz B2 poniżej). | | Profile Amazon / eBay / osobne zdjęcia | **Poza zakresem** — Faza C, gdy kanał realnie używany | --- ## Ryzyka — czego nie ignorować | Ryzyko | Skutek | |--------|--------| | Regulaminy kanałów (np. Allegro: niedozwolone słowa w tytule) | Obniżenie widoczności / interwencja platformy | | Halucynacje LLM (wymiary, materiał) | Zwroty, blokady konta | | Niespójność zdjęcie ↔ tytuł ↔ parametry | Oferta uznana za mylącą | | Kanały spoza BaseLinker-only (Amazon itd.) | Osobne limity i polityki publikacji | **Wdrożenie „od razu” (niskie ryzyko):** doprecyzowanie promptów, walidacja serwerowa, testy regresji — **bez** zmiany przepływu biznesowego (szkic → akceptacja → wysyłka). **Nie w jednym skoku:** pełna zgodność każdego marketplace pod każdym kątem (np. main image Amazon) — wymaga osobnych modułów (Faza C). --- ## Zasady «nie blądzimy» 1. **Granica zakresu:** ten dokument nie zawiera ID BaseLinkera, mapowań JTL ani procedur deploy — tylko odsyłacze. 2. **Jedna ścieżka walidacji tytułu kanału:** reguły Allegro (i przyszłe kanały) w **jednym** module testowalnym; wywołania z miejsca po `finalizeChannelsFromParsed` (ostrzeżenia przy generacji) oraz przy **zapisie / normalizacji** draftu (`normalizeDraftForSave` lub rozszerzenie `validateDraft`) tak, aby **ręczna edycja** tytułu w edytorze podlegała tym samym regułom co AI. 3. **Rozdział list:** `banlistPhrases` = treść sklepu / marka / clickbait. **Osobna** lista lub stałe **„Allegro title compliance”** (frazy z pomocy Allegro, długość, wyrazy) — **nie** mieszać w jednej tablicy bez komentarza źródła (`source: allegro-help` itd.). 4. **Regulamin zewnętrzny:** limity liczbowe (np. zakres znaków) **weryfikować kwartalnie** względem aktualnej strony pomocy Allegro; w kodzie przy sztywnych stałych—komentarz z datą weryfikacji i URL. 5. **Fazy:** każda faza ma **jeden mierzalny outcome** (patrz poniżej). --- ## Głos marki Dywanowy Bazar (operacyjnie) Wszystkie treści generowane przez AI są sterowane z **jednego pliku** [`config/editor-config.json`](../config/editor-config.json). Nie trzeba zmieniać kodu `openai.js`, żeby doprecyzować ton — wystarczy edycja konfiguracji i regresja testów (`npm test`). | Klucz | Rola | |-------|------| | `systemPrompt`, `globalRules` | Tożsamość marki, faktografia ze źródła, zakaz nazw producenta źródłowego (Tara), zakaz konkretnych kwot PLN w polach tekstowych kanałów, spójność tytuł ↔ opis. | | `banlistPhrases` | Marka / clickbait / frazy «hurtownia» — lista w promptach i czyszczenie po parsowaniu (`enforceBanlist`). | | `channels..titlePrompt`, `bodyPrompt` | **Ta sama prawda**, inna forma i limit znaków: Base (katalog), Allegro (transakcyjnie), Woo (SEO sklepu), OLX (ogłoszenie). | | `salesProfiles..promptAddon` | Ton przy wieloprofilowej generacji (np. formalny outlet Allegro vs SEO Woo). | | `copyStyles` | Odbiorca stylistyczny (świadomy zakup, rodzina ostrożnie itd.) — `promptAddon` per styl. | | `strategicCategories.lines..promptAddon` | Outlet vs DOM vs taras — dodatkowe instrukcje bez zmiany szkieletu `salesFramework`. | | `defaults.producer` | Domyślna nazwa producenta w polach (np. Bazarowy-Swiat); spójnie z regułami w `globalRules`. | --- ## Faza A — wdrożenie ścisłe (Allegro title) **Outcome:** operator widzi **jasny komunikat** przy problemie z tytułem Allegro (reguły zdefiniowane w kodzie + testy); draft może nadal być zapisywany, jeśli przyjmiecie tryb ostrzeżeń zamiast twardego bloku (decyzja produktowa). | ID | Zadanie | Kryterium akceptacji (AC) | |----|---------|---------------------------| | A1 | Moduł polityki tytułu [`src/channelTitlePolicy.js`](../src/channelTitlePolicy.js) | Funkcje czyste; wyjście = lista problemów (`severity`, `code`, `message`). Testy: [`test/allegro-title-policy.test.js`](../test/allegro-title-policy.test.js), rozszerzenie [`test/channel-titles.test.js`](../test/channel-titles.test.js). | | A2 | Podpięcie do pipeline | Po `finalizeChannelsFromParsed` (lub wewnątrz rozszerzenia wyniku) **oraz** w ścieżce zapisu draftu (`normalizeDraftForSave` / `validateDraft`) — te same reguły. | | A3 | UI | Ostrzeżenia/błędy widoczne dla operatora: minimum — wpisy w `meta.validationIssues` z polami rozpoznawalnymi dla kanału (np. `field` z prefiksem `channelTitle:` lub `allegroTitle`); docelowo — komunikat przy inpucie tytułu kanału **allegro** w [`public/editor.js`](../public/editor.js) (obecnie `renderValidation` nie mapuje `field` do konkretnego inputu — Faza A to uzupełnia). | **Uwaga:** punkt „jedno źródło prawdy dla faktów” **nie** jest osobnym ticketem wdrożeniowym — to **zasada utrzymaniowa:** prompty oparte o `extractFacts` / źródło, testy regresji przy zmianach w `src/openai.js`. Odświeżać przy każdej większej zmianie promptów. --- ## Faza B — krótki horyzont **Outcome:** spójniejsze opisy per kanał **bez** duplikowania `salesFramework`. | ID | Zadanie | Non-cel (żeby nie błądzić) | Status (2026-04) | |----|---------|----------------------------|------------------| | B1 | Szablony / kolejność sekcji per kanał | **Nie** budować drugiego silnika równoległego do `salesFramework.sections` — tylko **różnicowanie** `bodyPrompt` / konfiguracji kanału lub treści w snapshotach [`src/aiConfigSnapshots.js`](../src/aiConfigSnapshots.js). | **Jest** — blok `buildSharedChannelBodyContractBlock` w [`src/openai.js`](../src/openai.js) doklejany do `buildChannelPromptText` (wspólne fakty + `bodyPrompt` per kanał; Base = pełne 7 sekcji). Dalsze dopracowanie treści w `editor-config.json` / snapshotach bez nowego modułu sekcji. | | B2 | Checklista zdjęć | Statyczna lista w UI lub w odpowiedzi API; link do zasad Allegro: [Zdjęcia w galerii i w opisie](https://help.allegro.com/pl/sell/a/zasady-dla-zdjec-w-galerii-i-w-opisie-8dvWz3eo4T5). **Bez** automatycznej analizy obrazu na start. | **Jest** — [`public/allegroPhotoChecklist.js`](../public/allegroPhotoChecklist.js) + krok 3 kreatora + legacy `editor.html`. | | B3 | Testy regresji | Rozszerzenie testów o frazy zablokowane dla Allegro i granice długości — po wdrożeniu A1. | **Jest** — [`test/marketplace-copy-phase-b.test.js`](../test/marketplace-copy-phase-b.test.js) + istniejące [`test/allegro-title-policy.test.js`](../test/allegro-title-policy.test.js). | --- ## Faza C — gdy pojawi się kanał z osobnymi regułami **Outcome:** osobny profil (limity, znaki, pierwsze N znaków „mobile”) **tylko** dla kanału, który faktycznie publikujecie poza obecnym zestawem. - **Profil kanału:** rozszerzenie [`config/editor-config.json`](../config/editor-config.json) (`titleMaxChars` już jest per kanał). - **Zdjęcia:** oznaczanie roli (main / lifestyle), szablony eksportu (np. białe tło) — **tylko jeśli** kanał jest w użyciu. --- ## Definition of Done (globally dla tego planu) - Tytuł kanału **allegro** przechodzi walidację polityki **albo** każde naruszenie ma **czytelny** wpis zrozumiały w UI (`validationIssues` i/lub etykieta przy polu). - Brak regresji w [`test/channel-titles.test.js`](../test/channel-titles.test.js) i testach title generatora. - Krótka notatka dla operatora „co AI może / czego nie” — jedna sekcja w [`docs/PROGRESS.md`](PROGRESS.md) lub istniejącym README (bez kopiowania całego niniejszego pliku). **Stan po Fazie A:** [`validateDraftClient`](../public/editor.js) scala `channelTitlePolicy` per Allegro; [`renderValidation`](../public/editor.js) + komunikaty pod polem tytułu Allegro; serwer [`validateDraft`](../src/draft.js) — ten sam zestaw reguł; kreator — ostrzeżenia przy skrócie kanału Allegro (krok 5). --- ## Kolejność pracy w kodzie (z zależnościami) 1. **`src/channelTitlePolicy.js`** (lub równoważnik) + testy jednostkowe — **bez** podpinania do HTTP. 2. **`src/openai.js`** — konsumpcja po `finalizeChannelsFromParsed` (scalenie ostrzeżeń z istniejącymi `warnings`). 3. **`src/draft.js`** — walidacja przy `normalizeDraftForSave` lub `validateDraft` (spójność z zapisem). 4. **`public/editor.js`** — wyświetlanie przy polu tytułu **allegro** + **`public/wizard.js`** (krok 5, ten sam import policy). --- ## Diagram przepływu (jedna polityka, dwa punkty zaczepienia) ```mermaid flowchart LR subgraph gen [Generacja] AI[OpenAI JSON] Fin[finalizeChannelsFromParsed] AI --> Fin end subgraph persist [Zapis] Norm[normalizeDraftForSave] Val[validateDraft] Fin --> Norm Norm --> Val end subgraph ui [Edytor] Edit[channel title edit] Disp[issues przy polu] Edit --> Disp end Pol[channelTitlePolicy] Fin -.-> Pol Norm -.-> Pol ``` --- *Ostatnia aktualizacja planu: 2026-04-29 — Faza A zamknięta; Faza B (checklista zdjęć + kontrakt opisów) w kodzie; doprecyzowanie głosu marki w `editor-config.json` (per kanał, profile, linie strategiczne). Regulaminy marketplaceów zmieniają się — co kwartał zweryfikuj limity i listy fraz względem oficjalnej pomocy platform.*