29-05-2026 · v2.96
HLÍDÁNÍ VÝPOVĚDNÍCH LHŮT POJISTEK (DÁVKA 44)
Proaktivně: nový index partak/contracts_index.json + denní job contract_deadline_sweep (8:00). Spočítá výpovědní deadline (výročí − 42 dní, § 2807) a když se blíží (okno −7 až +30 dní), pošle do Telegramu alert s tlačítky: 📋 připravit na schůzku · ✍️ připravit výpověď · ✓ vyřešeno. Dedup per výroční cyklus.
Přidání: /smlouva Klient | Pojišťovna | DD.MM, výpis/odebrání /smlouvy, nebo přirozeně „hlídej výpověď u …" → token WATCH_CONTRACT. Výročí přijímá DD.MM / DD.MM.RRRR / YYYY-MM-DD (ukládá MM-DD, opakuje se ročně).
29-05-2026 · v2.95
TELEGRAM — FORWARD → AKCE + /klient_novy /poznamka (DÁVKA 43)
Přeposlání: přepošleš botovi e-mail/zprávu (detekce forward_origin) → nabídne tlačítka ✍️ návrh odpovědi · ✅ udělat úkol · 👤 uložit ke klientovi · 💬 jen zpracovat. Text se drží server-side, akce běží přes process_message.
Rychlá správa klienta: /klient_novy <jméno> založí klientskou složku a rovnou ho aktivuje; /poznamka <klient>: text zapíše do kontextu (UPDATE_KONTEXT). Bez jména vezme aktivního klienta.
29-05-2026 · v2.94
TELEGRAM — PDF DO CHATU + FOTO SMLOUVY (DÁVKA 42)
PDF jako soubor: vygenerované srovnání ŽP / porovnání nabídek / SAVE_AS_PDF přijde v Telegramu rovnou jako PDF dokument (ne jen Drive odkaz) — hned k přeposlání klientovi. Fronta _pending_outbound_files plněná orchestrátory, drainovaná po textu; pošle se i kdyby upload na Drive selhal.
Vícestránkové foto smlouvy: pošli strany jako album → Parťák je spojí do jednoho dokumentu (OCR per strana), u pojistné smlouvy (detekce _looks_like_contract) nabídne „📅 Spočítej výpovědní lhůtu" (§ 2807, výročí −42 dní). Funguje i u jedné fotky.
29-05-2026 · v2.93
TELEGRAM — PŘÍPRAVA NA SCHŮZKU (DÁVKA 41)
Proaktivně: ~60 min před klientskou schůzkou z kalendáře pošle Parťák do Telegramu upozornění s tlačítkem „📋 Připravit podklady →" (job meeting_prep_sweep každých 15 min, dedup per event). Ťuknutí spustí dossier.
Na vyžádání: /schuzka <klient> — shrne kde jste skončili, otevřené úkoly, nejbližší schůzku, blížící se výpovědní lhůty (§ 2807) a 3 návrhy témat. Reuse 3-zdrojového klientského kontextu (Todoist + Calendar + OneDrive/kontext.md).
29-05-2026 · v2.92
TELEGRAM — DOKUMENTY/FOTKY, PŘÍKAZY, TTS (DÁVKA 40)
Příjem souborů: v Telegramu teď pošleš PDF/DOCX nebo vyfotíš dokument — backend ho zpracuje stejně jako web upload (sdílená _ingest_document_bytes), fotky a skenované PDF projdou OCR. Pozná modelaci/Plaud/školení a nabídne tlačítkem co dál. Popisek u souboru se rovnou zpracuje.
Příkazové „/" menu: /help (kompletní nápověda po sekcích), /model (auto/haiku/sonnet), /usage (tokeny + náklady), /kontext (aktivní kontext + odpojení), /klient (aktivní klient), /mluv//ticho (hlasové odpovědi přes OpenAI TTS, opus → voice bubble).
Nápověda: web návod má teď u každé funkce řádek „📱 V Telegramu: …" a sekce Přílohy/Telegram vs. web jsou aktualizované.
29-05-2026 · v2.91
TELEGRAM — KLIKACÍ INLINE TLAČÍTKA
A/B/C bloky na konci odpovědí se v Telegramu vykreslí jako opravdová ťukací tlačítka (InlineKeyboardMarkup), ne jen text k opisování. Prompt se kvůli 64B limitu callback_data drží server-side pod krátkým klíčem; po kliku se původní tlačítka zakážou.
29-05-2026 · v2.90
DOVEDNOSTI — HYBRID + NÁVRHOVÁ TLAČÍTKA (DÁVKA 39)
Hybrid model: tiše se samy aktivují jen výchozí klient-dokumenty a plaud-zaznam. Ostatní (brand, spory, web-*, školení) se už nezapínají potají — Parťák je nabídne tlačítkem „Zapnout … a zopakovat" (zapne dovednost a rovnou zopakuje dotaz, /skill_redo). Příklad: „srovnej modelace" → nabídne brand RESPECT FINANCE.
Fix: na mobilu/Androidu se po loginu nenačítaly DOVEDNOSTI (refresh běžel jen před přihlášením) — opraveno voláním v enterApp().
28-05-2026 · v2.89
CLAUDE CLI — ROBUSTNÍ RESOLVER CESTY (DÁVKA 38)
Improvement log: SROVNANI_ZP_PDF padal s „⚠️ Claude CLI nenalezen na /home/partak/.npm-global/bin/claude" — pevně zadaná cesta selhala při chybějící / přeinstalované instalaci CLI (FileNotFoundError). Modelace se rozpoznaly správně (10× „…modelace.pdf"), padla až extrakce dat Sonnetem přes CLI.
Resolver místo hardcoded cesty: _resolve_claude_cli() hledá CLI v pořadí: env PARTAK_CLAUDE_CLI → claude na PATH → známé cesty (~/.npm-global/bin, ~/.local/bin, /usr/local/bin, /usr/bin). Vezme první, co existuje a je spustitelné. Chybová hláška je teď akční — když je CLI jinde, poradí kam a ať se restartuje služba.
Otestuj: přilož 10 modelací ŽP (nebo aktivuj klientskou OneDrive složku s nimi) a požádej o srovnání — extrakce přes CLI teď projde až k PDF.
28-05-2026 · v2.88
MODELACE — ROZPOZNÁNÍ + ZLOM STRÁNEK V PDF (DÁVKA 37)
Rozpoznání modelací: dokument se slovem Návrh / Nabídka / Modelace / Kalkulace / Srovnání v názvu nebo v prvních ~10 řádcích Parťák bere jako jednu skupinu „Modelace" (podklad pro srovnání). Má přednost před Plaud heuristikou — modelace s řádky typu „Pojistná částka:" / „Klient:" se už nesplete se zápisem z Plaudu. Při uploadu místo Plaud nabídky naskočí „📊 Vypadá to jako modelace…"; u 2+ rovnou tlačítko na vytvoření srovnání.
Modelace z OneDrive složky: SROVNANI_ZP_PDF teď při málo přílohách sám stáhne modelace z aktivní klientské OneDrive složky (_collect_onedrive_modelace_docs) — nemusíš je vkládat do chatu, stačí složku vybrat. Počítá přílohy i složku dohromady (2+ → spustí token).
Zlom stránek v PDF srovnání: sekce „Co která pojišťovna nabízí" a „Návrh dalšího postupu" začínají vždy na nové straně; „Celkové měsíční pojistné" drží s hero bannerem na straně 1. Nadpis se už neosiří na konci strany.
Otestuj: přilož soubory s „modelace" v názvu — měl by je vzít jako modelace (ne Plaud). Pak „udělej srovnání ŽP pro klienta X" a zkontroluj zlomy stránek v PDF.
21-05-2026 · v2.82
PER-DOC CAP V HLAVNÍM KONTEXTU (DÁVKA 31)
Improvement log #4 (Vícena Pavel, znova): Sonnet 4.6 vrátil „Prompt is too long" — ~893 tisíc znaků (12 zpráv v paměti, 3 aktivní dokumenty). 3 PDF × 50k znaků preview + history + system prompt přesáhly Sonnetův 200k token limit. Token SROVNANI_ZP_PDF se nikdy nevygeneroval, protože **hlavní Parťákova odpověď** Sonnetem nezvládla. Sonnet 1M context variant vyžaduje placený plán Anthropic (memory feedback_no_paid_api to zakazuje).
Per-doc cap v hlavním kontextu: v build_documents_context() nový lokální cap PER_DOC_IN_CTX = 25_000 (poloviční oproti MAX_DOC_CHARS = 50_000). Pro 3 PDF zabere hlavní kontext maximálně 75k místo 150k znaků — zůstane rezerva pro history (~60k) + system prompt (~10k).
Plný preview (50k) zůstává v d['preview'] a tedy dostupný pro extract handlery (SROVNANI_ZP_PDF, COMPARE_OFFERS) přes _build_compare_documents_block() i pro klient-dokumenty fulltext search. Kompromis: hlavní konverzace vidí hlavičku + 25k znaků doc; extract má plný kontext. V hlavním kontextu se objeví hláška „doc oříznut na 25 tisíc znaků z 50 tisíc; plný obsah dostupný v extract handlerech".
Otestuj: přilož 3 PDF nabídek a požádej o SROVNANI_ZP_PDF. Sonnetu hlavní call už nesmí přetéct kontextem, token vyletí, extract dostane plný preview, PDF se uloží.
21-05-2026 · v2.81
PDF — BRAND DEFAULT + ZÁKAZ FABRICOVANÝCH DAT (DÁVKA 30)
Improvement log #3 (Vícena Pavel, znovu): Parťák po resetu kontextu (přílohy zmizely z `active_documents`) místo žádosti o znovupřiložení **vyrobil náhradní obsah z `conversation_history`** a poslal ho přes SAVE_AS_PDF. Důsledek: PDF má jen pár řádků dat z paměti, ne plný obsah z reálných nabídek. Plus se vyrenderoval **BEZ brandu**, ač Martin měl skill `brand-respect-finance` zapnutý — auto-detekce přes `skills.is_active(...)` nefungovala spolehlivě.
Refactor SAVE_AS_PDF — brand vždy default. Auto-detekce přes skill zrušena. Token nově má volitelný 5. parametr |<mode>: bez 5. parametru = RF brand (Martin → klient: analýzy, kalkulace, doporučení), s |klient = neutral Times serif (klient → instituce: výpovědi, žádosti). Grammar: SAVE_AS_PDF|<klient>|<filename>|<content>[|<mode>]. Token handler parsuje 5. parametr přes split('|', 4).
CLAUDE.md zákaz fabricovaných dat pro SROVNANI_ZP_PDF: když po resetu kontextu `active_documents` chybí, Parťák musí říct „Kontext byl resetnut, přilož znovu 3 PDF nabídek" — nesmí použít SAVE_AS_PDF s daty z `conversation_history` jako náhradu. Reálná čísla pojistného a krytí MUSÍ pocházet z PDF, ne z paměti.
Lekce: auto-detekce stavu jako skill je nestabilní — uživatel si ho přepíná v UI bez vědomí dopadů na render. Explicitní parametr v tokenu je deterministický a auditovatelný (LLM ho viditelně píše, Martin v Telegram odpovědi vidí výsledek). Brand default je správné — větší část dokumentů Parťák tvoří jménem Martina, jen výpovědi jsou explicitně klientův dokument.
21-05-2026 · v2.80
SROVNANI_ZP — OPRAVA SIGNATURY CLAUDE CALLERU (DÁVKA 29)
Improvement log #2 (znovu Vicena Pavel): SROVNANI_ZP_PDF padl rovnou v Sonnet callu — call_claude_async() got an unexpected keyword argument 'system'. Šťastná „regrese" — díky lepšímu error reportingu z v2.79 Martin viděl konkrétní příčinu (TypeError) místo generic hlášky.
Root cause: call_claude_async má signature (messages, system_prompt, ...) — POSITIONAL parametr system_prompt, ne keyword system. partak/pdf/extract.py jsem v dávce 27 napsal naslepo bez ověření existující signatury a volal claude_caller(system=...). Fix: positional call claude_caller([msg], system_prompt, ...).
Lekce: dependency injection s typed callable (Awaitable) zachytává jen signature mismatch při explicit type hint — Python defaultně argument names nekontroluje. Smoke test v dávce 27 byl jen na render+upload (mock data), neproběhl plný E2E přes Sonnet, jinak by se to chytlo. Příště: i pro mock test injektovat reálný caller s minimálním vstupem.
21-05-2026 · v2.79
SROVNANI_ZP — MEZIPRODUKTOVÁ ANALÝZA + DEBUG LOG (DÁVKA 28)
Improvement log #1 — Vicena Pavel: SROVNANI_ZP_PDF selhal s hláškou „Sonnet vrátil neparsovatelná data nebo méně než 2 produkty". Vstup byly 3 PDF: GČP modelace Bel Mondo, Koop sjednaná smlouva FLEXI a Broker Trust meziproduktová analýza (24 stran). Z prvních dvou Sonnet vytáhl jen 1 produkt každý, **meziproduktovou analýzu (typ c) si neuvědomil jako zdroj více cards** a vrátil < 2 produkty. Plus error reporting handler shrnul vše do jedné generické hlášky — Martin nevěděl, který krok padl.
Prompt rozšířen o typy vstupů (extract.py pravidlo 15): a) modelace pojišťovny (1 vendor → 1 card), b) sjednaná smlouva klienta (1 card + `is_current: true`, badge „Stávající"), c) **meziproduktová analýza** (BeTy / Broker Trust / OK Group — 1 dokument obsahující 5+ produktů → Sonnet z ní VYTÁHNE více cards i coverages), d) klientský dotazník (jen klient.meta_extra, žádné cards). Cíl je vždy ≥ 2 cards.
Refactor return signature extract_srovnani_zp_data → (data, error) tuple. Při selhání vrátí konkrétní důvod (Sonnet exception / empty response / JSON parse fail / no price_section / „jen N produktů"). Handler v generate_srovnani_zp_pdf reportuje Martinovi přesný důvod + počet a názvy dokumentů + velikost vstupu.
Debug log partak/pdf_extract_debug.jsonl — při každém selhání se uloží append-only záznam s timestamp, klient hint, docs_chars a raw_excerpt (3000 znaků Sonnetova outputu). Retrospektivní diagnostika bez nutnosti reprodukovat scenario.
Test: Martin může zkusit Vicena Pavel znovu — s 3 stejnými přílohami by Sonnet měl Broker Trust analýzu rozparsovat na 7 cards (Allianz, Generali ČP, NN, Kooperativa, UNIQA, ČPP, MetLife podle té analýzy obvykle), Koop smlouvu označit `is_current` a GČP modelaci jako další produkt. Pokud problém přetrvá, hláška řekne přesný důvod a debug log zachová Sonnetův raw output.
21-05-2026 · v2.78
SROVNANI_ZP_PDF — JSON VSTUP + JINJA2 (DÁVKA 27)
Strukturovaný JSON vstup pro typ srovnani_zp. Brief sek 6 specifikoval bohaté komponenty (price-card grid s `current`/`cheapest` variantami, coverage-card grid s highlight/warning řádky, recommendation box), které markdown→PDF (SAVE_AS_PDF) elegantně neumí. Nový token SROVNANI_ZP_PDF|<klient>|<filename> bere 2+ přiložené nabídky životního pojištění z active_documents, Sonnet 4.6 z nich extrahuje strukturovaný JSON, Jinja2 šablona vyrendruje bohaté brandové PDF.
Nový modul partak/pdf/: __init__.py (export), render.py (Jinja2 + weasyprint orchestrátor), extract.py (Sonnet 4.6 JSON schéma + extract funkce s dependency injection), templates/srovnani_zp.html.j2 (~300 řádků Jinja2 + CSS). Jinja2 jako nová dep (3.1.6), přidána do partak-env. Reference: partak/pdf/samples/srovnani-zp-new.html (898 řádků zlatého standardu).
JSON schéma (Sonnet 4.6 vrací): client (name + meta_extra povolání/rodina), alert (volitelný — modelace omezení), price_section (cards seřazené od nejlevnější, is_cheapest/is_current flagy + badge), coverages_section (4–6 krytí s rows highlight/warning + note), recommendation (preferred produkt + body + next_steps). Sonnet sám formátuje čísla (úzká mezera, Kč za částkou, % s mezerou), zachová oficiální názvy pojišťoven (Allianz, Generali ČP, NN, Kooperativa, UNIQA, ČPP, MetLife), doplní diakritiku.
Layout fixy oproti naivnímu Jinja2 render: CSS Grid auto-fit minmax weasyprint nepodporuje dobře (cards padaly do single column) → použit flexbox flex-wrap s flex: 0 0 calc(50% - 5pt). Body bez flex layout + bez min-height: 297mm (jinak weasyprint ztrácel recommendation/footer při page-flow). Jinja2 pitfall data.coverages_section.items = dict method, ne klíč → použito data.coverages_section['items'].
Smoke test s Cvíková Eva mock daty (7 pojišťoven, 4 krytí, Kooperativa FLEXI doporučení) prošel: hero, alert, price-cards 2 sloupce (UNIQA gold border, Allianz Život navy), coverages 2x2 grid (highlight gold, warning red), recommendation navy box s Playfair gold titulkem, brandový footer s mottem. 4 strany pro tento obsah, footer na page 4 (akceptovatelný trade-off — reference Cvíkové má 11 stran).
Co stále chybí oproti briefu sek 6: comparison-table (velký souhrn všech parametrů × pojišťoven), pros/cons panely (zelený/červený gradient vedle sebe), benefits-grid (extra benefity per pojišťovna). Pro plnou paritu s Cvíkovou referencí — řeší se v navazujících dávkách. Token zatím použít jako MVP pro klientská srovnání ŽP.
21-05-2026 · v2.77
PDF — TABULKY + ALERT/RECO BOX + FOOTER DOLE (DÁVKA 26)
Tři konkrétní stížnosti od Martina po v2.76: (1) markdown tabulky se nerenderovaly jako tabulky, vyšly textově (oddělovače `|` zůstaly); (2) navy footer se neukotvil dole na poslední stránce, vyplynul hned za sekcí; (3) chyběly brandové prvky jako v reference Cvíkové HTML (alert box pro důležité poznámky, recommendation box pro závěr).
Markdown tabulky GFM-style v _md_to_html_lite: detekce header řádku | col | col | + separator |---|---| + následujících rows. Render <table><thead><tr><th>…</th>…<tbody><tr><td>…. CSS: navy header s bílým textem, padding 8/11pt, alternující řádky cream/bílá, **bold** v navy. Tabulka má page-break-inside: auto + thead display: table-header-group — při zlomu stránky se hlavička opakuje.
Footer na spodku poslední stránky. Body display: flex; flex-direction: column; min-height: 297mm, section flex: 1 0 auto, footer margin-top: auto; flex-shrink: 0. Krátký obsah → footer dole na první stránce. Dlouhý obsah → tabulka přeteče, footer dole na poslední stránce. Weasyprint respektuje flex layout, žádný `position: absolute` hack potřeba.
Alert/Recommendation komponenty přes markdown blockquote syntax: > [!ALERT] Titulek
> tělo renderuje box s cream gradient pozadím + gold border-left + navy bold titulkem. > [!RECO] Doporučení
> tělo renderuje navy box s Playfair gold titulkem a gold-soft akcentem pro **strong**. Žádný nový token — Parťák prostě generuje markdown a backend rozezná callout pattern. Smoke test E2E: 2-stránkové PDF s tabulkou 7 pojišťoven, alert + reco — vše drží dle reference Cvíkové.
Co ještě chybí oproti briefu sek 6: price-card grid (cards s `current`/`cheapest` variantami), coverage-grid (krytí per pojišťovna), pros/cons panely, benefits-grid. Tyto vyžadují strukturovaný JSON vstup, ne jen markdown — řeší se v navazující dávce s novým typem dokumentu srovnani_zp.
21-05-2026 · v2.76
PDF BRAND — HERO + FOOTER + MARTIN (DÁVKA 25)
Brief od Martina (21. 5.): dnešní SAVE_AS_PDF brand verze (v2.75) vypadala vizuálně neutrálně — Playfair + DM Sans bez hierarchie, žádný hero, žádný footer s identitou. Brief jako zlatý standard přiloženo brandové HTML pro Cvíková Eva — Srovnání ŽP (898 řádků, navy gradient hero + gold eyebrow + meta grid + section + recommendation box + brandový footer s mottem). Zařazeno do partak/pdf/samples/ jako trvalá vizuální reference (s allowlistem v .gitignore).
Upgrade _SAVE_PDF_BRAND_CSS přesně dle reference: @page bez okrajů + body padding (hero/section/footer vyplňují A4 od kraje ke kraji), CSS custom properties (--navy, --gold, --cream…). Hero = navy gradient 135° + gold eyebrow s typem dokumentu + Playfair Display H1 (26pt) kde poslední slovo je v gold italic akcentu + meta grid 3 sloupce (Klient / Datum / Zpracoval). Section = Playfair H2 s krátkou gold accent line, DM Sans tělo (11pt), strong v navy, kódování českých čísel 1 000 000 Kč. Footer = navy + Martin Hrňa (Playfair) + RF kontakty s gold accenty + motto „Klientovi prospěch, sobě klid." v gold Playfair italic na střed.
Neutral mode (skill OFF) zachován minimalistický — Times New Roman, žádný brand, jen .client-block box. Pro dokumenty, které podepisuje klient a posílá pojišťovně/bance/soudu (např. výpověď GČP smlouvy). Žádné RF stopy v dokumentu, který klient pošle jménem sebe.
Page-break pravidla (per brief sek 8): .hero { page-break-after: avoid }, .footer { page-break-inside: avoid }, h2/h3 { page-break-after: avoid } — sekce a nadpisy se nepřerušují na nesprávných místech. Smoke test E2E: render Analýza portfolia (5 sekcí, list, číslovaný list, bold/italic) → strana 1/1, hero + footer drží, čitelnost OK.
Co je dál: v navazujících dávkách (b) typ Srovnání ZP s JSON vstupem a price-grid/coverage-grid jak v Cvíková reference; (c) refactor do modulu partak/pdf/ s Jinja2 templates + partials. Dnes jen brand upgrade existujícího SAVE_AS_PDF — žádný regresní impact na compare-offers PDF a generický markdown→PDF, jen vizuální obohacení.
21-05-2026 · v2.75
PDF EXPORT — SAVE_AS_PDF TOKEN (DÁVKA 24)
Improvement log #1 — Maxa Lukáš, výpověď GČP smlouvy. Martin chtěl uložit text výpovědi jako PDF; Parťák odmítnul slovy „PDF generování bohužel nemám v dostupných tokenech" a nabídl jen e-mailový koncept / ruční Word konverzi. Přitom weasyprint i reportlab jsou v partak-env (COMPARE_OFFERS PDF už weasyprint používá od v2.68). Chyběl jen token a handler.
Token SAVE_AS_PDF|<klient>|<filename.pdf>|<markdown> jako instant token v pre-pass (analog UPDATE_KONTEXT — žádné ano/ne, backend uloží rovnou). Content podporuje lehký markdown: #/##/### nadpisy, **bold**, *italic*, seznamy - nebo 1.. Vlastní mini parser _md_to_html_lite, bez externí závislosti (markdown package není v env).
Brand vs no-brand z aktivního skillu. Bez A/B dialogu: backend deterministicky zkontroluje skills.is_active('brand-respect-finance'). Skill ON → RF brand (navy/gold, Playfair Display + DM Sans, patička s mottem „Klientovi prospěch, sobě klid."). Skill OFF → čistá serif Times New Roman, žádný brand — pro dokumenty, které podepisuje klient a posílá pojišťovně/bance/soudu (např. výpověď). Martin si přepíná skill v UI podle kontextu.
Upload do _PARTAK/klienti/<klient>/<filename>.pdf přes existující save_client_document (klient.py). Filename se sanitizuje (_slugify_filename odstraní diakritiku a mezery), .pdf se doplní pokud chybí. CLAUDE.md má novou sekci „Export do PDF" se striktním pravidlem: nikdy nepřesměrovávat Martina na e-mailové koncepty nebo ruční Word — token existuje, použij ho.
21-05-2026 · v2.74
HEALTH ENDPOINT BEZ SIDE-EFFECTS (DÁVKA 23)
Smoke test post_init bez spamu na Telegram. Při ladění dnešní opravy scheduleru (v2.71) jsem opakovaně volal POST /api/briefing/run jako kontrolu, jestli post_init proběhl — jenže ten endpoint reálně posílá Telegram zprávu, takže ti dnes ráno dorazil briefing 4×. Nový GET /api/health vrátí {bot_ready, scheduler_running, jobs: [{id, next_run}], pid, uptime_sec, session_start, now} — pure read, žádné Telegram volání. Dostupný i veřejně přes nginx (https://partak.respect-finance.cz/api/health) bez auth, jako /api/usage.
Globální _scheduler_ref. APScheduler instance byla dosud lokální proměnná v post_init — endpoint by ji neviděl. Přidán globální ref nastavený po scheduler.start(), aby šly vypsat aktivní joby a jejich příští spuštění. Když je scheduler_running: false, post_init buď nedoběhl, nebo padl exception po nastavení _telegram_bot_ref.
21-05-2026 · v2.73
TODOIST — SEKCE KLIENTA JAKO ŠPENDLÍK (DÁVKA 22)
Cowork dříve uklidil Todoist do nové struktury — projekt PLAUD má pro každého klienta sekci 📌 Jméno Příjmení a úkoly mají prefix Příjmení – content (em-dash). Parťák to ale neuměl: add_todoist_task generoval starý format [Klient: Příjmení Jméno] content + slug label klient-prijmeni-jmeno, navíc Plaud-generované úkoly padaly do Inboxu místo PLAUD. Po dnešním Martinově požadavku přepsán celý kontrakt.
Nové helpery: client_to_section_name('Vajgert Martin') → '📌 Martin Vajgert' (reverse split z kanonického „Příjmení Jméno"), get_todoist_sections(project_id) s cache, ensure_section(project_id, name) auto-vytvoří sekci pokud chybí. format_task_with_client přepsán na Příjmení – content s detekcí duplicity a převodem starého [Klient: …] prefixu.
Routing: add_todoist_task s klientem a bez explicitního projektu → default Do práce 🎯. Plaud workflow (_execute_plaud_proposal) explicitně předává project_name='PLAUD' + client_name. V obou případech ensure_section nastaví správnou sekci, label klient-slug se už nepřidává — sekce nahrazuje špendlík.
Dual match v _get_klient_tasks — match v pořadí: 0) section_id (nový), 1) prefix Příjmení – (nový), 2) legacy [Klient: …], 3) legacy label, 4) fuzzy příjmení. Smoke E2E test prošel oba scénáře: existující sekce Mareš v PLAUD se použila; nový klient „Testovský Petr" → automaticky založeno 📌 Petr Testovský v Do práce 🎯, úkol Testovský – ... v ní.
21-05-2026 · v2.72
ČESKÝ KALENDÁŘ SVÁTKŮ Z LOOKUP TABULKY (DÁVKA 21)
Claude Haiku v ranním briefingu opakovaně halucinoval svátek. 21.5. řekl „Konstantin, Helena" místo „Monika" (Konstantin je v slovenském kalendáři, Helena je 18.8.). Náhrada za call_claude_async deterministickou lookup tabulkou v partak/jmeniny.py — 366 dní občanského kalendáře v dict {(month, day): "jméno"}, helper jmeno_pro_den(date). Žádný LLM call, žádný timeout, žádná halucinace.
21-05-2026 · v2.71
SCHEDULER FIX (PTB 22.7) + /HELP NÁPOVĚDA (DÁVKA 20)
Dnes ráno nepřišel automatický briefing. Diagnostika: bot běžel, FastAPI odpovídal, ale _telegram_bot_ref is None 7+ minut po startu — žádné Telegram zprávy, žádný scheduler. Root cause: upgrade PTB na 22.7 přesunul volání post_init jen do run_polling()/run_webhook() — náš main_async jede manuální initialize() + start() + start_polling() paralelně s uvicornem (kvůli FastAPI), tj. post_init se nikdy nezavolal. Důsledek: scheduler nestartoval → ranní/odpolední/večerní briefingy nikdy nevyletěly.
Fix: run_telegram() teď volá await post_init(app) ručně mezi initialize() a start(). _telegram_bot_ref se navíc nastavuje jako úplně první řádek post_init (fail-safe pro smoke endpoint kdyby cokoli níže padlo).
Vedle toho dokončena help feature: _is_help_query + _load_help_text v backendu + dokumentace v CLAUDE.md. Když napíšeš „jak ovládat", „/help" apod., do system promptu se vloží blok z partak/help_telegram.md a Parťák odpovídá podle něj místo halucinace o vlastních funkcích.
20-05-2026 · v2.70
BEZPEČNOST — INCIDENT REPORT PO LOGINU (DÁVKA 19)
Martin chtěl, aby po blokaci a prvním úspěšném loginu dostal popis situace včetně časů chybných pokusů a pokud možno IP. Při zkoumání kódu jsem našel bug: backend čte req.client.host, což za nginx proxy znamená 127.0.0.1 — emaily „IP X se neúspěšně přihlásila" tedy chodily s 127.0.0.1 místo reálné IP útočníka. Nginx přitom X-Real-IP i X-Forwarded-For v hlavičkách posílá (/etc/nginx/sites-available/partak řádky 25–26), backend je jen nečetl. Druhý problém: failed_attempts[ip] = [count, blocked_until] uložilo jen counter, žádné timestamps. Po restartu Parťáka se incident history úplně ztrácela (in-memory dict).
Helper _get_client_ip(req) teď čte primárně X-Real-IP, fallback první v X-Forwarded-For, pak req.client.host. Helper _is_private_ip(ip) rozpozná loopback / 10.* / 192.168.* / 172.16-31.* a vrátí True, aby se zbytečně nevolala geo lookup pro lokální síť.
Geo lookup přes ip-api.com (free, žádný API key, 45 req/min). Vrací město + zemi + ISP + ASN. Cache v _geo_cache s TTL 1 h per IP (typicky útočník zkouší PIN několikrát z téže IP — jeden lookup za hodinu stačí). Timeout 3 s, při chybě/timeoutu vrátí None a neselže auth flow. Volá se přes httpx.AsyncClient (httpx 0.28.1 už v partak-env).
Persistent log partak/auth_attempts.jsonl. Append-only JSONL, každý pokus dostane vlastní řádek: {ts, ip, user_agent, result, geo, attempts_in_window}. result ∈ success / fail / blocked (= ten 3. pokus, který spustil 15min lockout) / during_block (= pokus během lockoutu, vrátil 429). Soubor přežije restart, takže Parťák vidí historii dále.
Incident report po úspěšném loginu. _read_recent_auth_attempts(ip, hours=24) načte poslední záznamy pro tuto IP. Pokud aspoň 1 měl result fail/blocked/during_block, sestaví se text přes _format_incident_report a pošle se dvěma kanály paralelně: (1) WS broadcast {type: 'security_alert', text: ...} → frontend rendruje jako assistant bubble v chatu hned po loginu; (2) Telegram zpráva přes existující _send_telegram_with_retry. Při dosažení limitu blokace (3× špatně) jde Telegram zpráva okamžitě (Martin možná není v chatu, ale push notifikaci na telefonu uvidí).
User-Agent přes _short_user_agent. Zkracuje na lidský tvar (Chrome / Android, Edge / Windows) místo plného UA stringu. Pomocná značka „stejné zařízení" v reportu, pokud IP + UA souhlasí s aktuálním úspěšným loginem (typicky když si Martin sám špatně napíše PIN — odliší se to od reálného útoku).
Email upozornění (předtím přes _notify_brute_force) byl vyřazen ve prospěch Telegram + chat bubble — Martin tyhle dva kanály preferuje. Pokud chce Martin vrátit email jako třetí redundantní kanál, stačí přidat asyncio.create_task(asyncio.to_thread(send_email, ...)) do bloku if attempts >= BLOCK_AFTER.
20-05-2026 · v2.69
MULTI-TOKEN + PROSE-TOLERANT ROUTING (DÁVKA 18)
Hainc-like bug v token routingu. Při incidentu se Soukupem Tomášem (20.5.2026, 19:06) Parťák správně emitnul dva tokeny v jedné odpovědi — SAVE_EMAIL_ATTACHMENTS_GD|INBOX|1794|Soukup Tomáš a UPDATE_KONTEXT|Soukup Tomáš|produkty|RB nabídka..., předem napsal prózu „Uložím přílohy z emailu a zapíšu nabídku od RB do kontextu Soukupa Tomáše." Backend ale token routing dělal přes assistant_message.startswith("TOKEN|") — kontrola jestli celá zpráva začíná tokenem. Začínala prózou, takže ani jeden token nematchnul. Příloha se neuložila, kontext nezapsal — a Parťák si myslel, že je hotovo. Druhá facet bugu: i kdyby prose-prefix tolerance fungovala, kód po prvním matchnutém tokenu volá return a ostatní tokeny ignoruje.
Sjednocená detekce přes _msg_token_line(). Nová helper funkce skenuje řádky a vrátí první řádek (stripped) začínající daným prefixem — token se najde i s prózou před ním. Postižené handlery (CALENDAR_EVENT, EMAIL_DRAFT, EMAIL_SEND, SAVE_EMAIL_ATTACHMENTS, SAVE_EMAIL_ATTACHMENTS_GD) přešly z startswith na nový helper. MEETING_SUMMARY, LOG_WRITE, COMPARE_OFFERS, UKLID_FOLDER už prose-tolerant byly (line-scan).
Multi-token pre-pass. Před hlavním routingem běží pre-pass, který zpracuje všechny instant tokeny v jedné zprávě (bez ano/ne dialogu): UPDATE_KONTEXT, MOVE_FILE, MOVE_FILE_SKOLENI, RENAME_FOLDER, TODOIST_ADD, TODOIST_COMPLETE, PIN_INFO, UNSET_ONEDRIVE_FOLDER. Každý token se vykoná (zápis do kontext.md, přesun souboru, …), výstup se přidá do _instant_outputs. Po pre-pass se zpráva strip-ne (token řádky pryč) a hlavní routing pokračuje na pending tokenech (CALENDAR_EVENT, EMAIL_SEND, SAVE_EMAIL_ATTACHMENTS*, MEETING_SUMMARY, TRAINING_*). Finální odpověď spojí instant outputs + pending response přes _wrap_response() s separátorem \n\n---\n\n.
Použití na Soukupův případ. Stejná zpráva po opravě: pre-pass zpracuje UPDATE_KONTEXT (zápis do _PARTAK/klienti/Soukup Tomáš/kontext.md sekce „produkty"), pak hlavní routing nachází SAVE_EMAIL_ATTACHMENTS_GD a vrací dialog „Mám uložit 1 přílohu z INBOX#1794 do _PARTAK/klienti/Soukup Tomáš? (ano/ne)". Martin vidí: „📝 kontext.md klienta Soukup Tomáš aktualizován (1 sekce: produkty)\n\n---\n\nMám uložit 1 přílohu…". Po ano se přílohy stáhnou a uloží.
Bezpečnostní pojistka: token MOVE_FILE_SKOLENI| má jiný oddělovač (_ místo |) — nematchne tedy prefix MOVE_FILE|. Strip-token-lines kontroluje explicitní startswith každého prefixu zvlášť. Sanity smoke test ověřil oba scénáře.
20-05-2026 · v2.68
POROVNÁNÍ NABÍDEK DO PDF + RETRY (DÁVKA 17)
(1) Nový skill compare_offers_pdf. Martin přiloží 2+ nabídek (pojistné, hypoteční, úvěrové, investiční) přes kancelářskou sponku, řekne „porovnej tyto nabídky" — Parťák vrátí token COMPARE_OFFERS|<mode>|<note>. Backend vezme active_documents s preview, volá Sonnet 4.6 s extrakčním promptem → JSON (sjednocené parametry napříč produkty, výhody/nevýhody per produkt, recommendation nebo neutral_summary). Renderuje HTML šablonu s brandem RESPECT FINANCE (navy #0a1628 / gold #C8963E, Playfair Display + DM Sans, patička s mottem) a přes weasyprint 68.1 vyrobí PDF. Upload do _PARTAK/porovnani-nabidek/<YYYY-MM-DD>_<slug>.pdf přes existující upload_to_partak_subfolder, Drive odkaz se vrátí Martinovi. Dva režimy striktně podle pravidla 6 z dávky 1 (sekce Brand v partak/CLAUDE.md): interni (RF brand, Martin → klient) a klient (no-brand serif šablona — dokument za klienta, který ho pošle pojišťovně/bance/soudu). Parťák se Martina zeptá A/B před tokenem (default je interní); pokud Martin výslovně řekne „pro klienta", vrátí rovnou klient mode. Bez instalace nových knihoven (reportlab/weasyprint už v partak-env).
(2) Hainc bug fix — robustní JSON parser. Při úklidu klienta Hainc Ondřej frontend padal s hláškou „NETWORK: Unexpected token <..." — backend operace prošla, ale nginx proxy timeoutoval a vrátil HTML error page; frontend volal slepě await r.json(). Nový helper _safeJson(resp) detekuje content-type, při HTML odpovědi vytáhne textový snippet (bez tagů) a vrátí čitelnou hlášku „gateway timeout — backend operace běžela příliš dlouho" / „backend selhal (502)" místo cryptic JSON parse erroru. Aplikováno na /api/uklid/execute a /api/uklid/delete_backup.
(3) Automatický retry pro transient chyby. Hainc úklid se podařil až na druhý ruční pokus — backend dokončil operaci, nginx timeoutoval. Nový helper _fetchJsonWithRetry(url, opts, onAttempt) automaticky zkusí jeden retry po 3s, pokud je chyba transient (5xx, gateway timeout, network drop). UI tlačítko během retry ukazuje „· POKUS 2…". Po druhém selhání hláška „selhalo i po retry: <důvod>". Idempotence: úklid execute generuje nový _backup-* timestamp pro každý pokus; delete_backup je idempotentní z definice (Graph DELETE neexistující složky vrátí 404 = non-transient). 4xx (client error) se NEretryuje — backend říká „nepošli takhle".
20-05-2026 · v2.67
ÚKLID — PŘEJMENOVÁNÍ + SMAZÁNÍ ZÁLOHY (DÁVKA 16)
Tři vylepšení nad úklid dialogem: (1) Smazání zálohy z UI — po úspěšném přesunu se ▸ PROVEĎ ÚKLID přepne na 🗑 SMAZAT ZÁLOHU s tooltipem cesty zálohy. Klik (po confirm dialogu) volá nový endpoint POST /api/uklid/delete_backup a smaže _backup-YYYYMMDD-HHMMSS/ do OneDrive koše (30 dní obnovitelné přes web UI). Backend dvě vrstvy guardu: sanity-check, že backup_path začíná <folder_path>/_backup- (nelze tudy smazat libovolnou složku), a allowlist guard v delete_item helperu (cesta musí být v ONEDRIVE_ALLOWED_PATHS.txt). Po smazání tlačítko přejde na disabled „✓ HOTOVO" — terminální stav. (2) Přejmenování při přesunu — každý řádek tabulky má teď <input> se základem názvu místo static <span>. Koncovka (.pdf, .docx, …) je vedle inputu jako šedý text a automaticky se připojí — Martin ji needituje, nemůže ji omylem ztratit. Frontend posílá new_name v assignments jen pokud se basename reálně změnil. Backend (apply_client_folder_moves) předává new_name do move_file → Graph PATCH s {name, parentReference} v jediném requestu = atomický rename+move. Manifest moves[i].renamed_to, badge řádku ✓→ s tooltipem. Vícenásobné koncovky (.tar.gz) se splitnou jen na poslední tečce — Graph je se zachováním celého basenamu. (3) Helper delete_item v partak/onedrive.py (DELETE /me/drive/items/{id}, allowlist guard přes _path_for_item_id). Pravidla v partak/CLAUDE.md sekce „Úklid OneDrive klientských složek" budou updatnuta dávkou 16b — aktuálně platí: Parťák zálohu nikdy nesmaže sám, mazání jen přes UI tlačítko po Martinově kliknutí.
20-05-2026 · v2.66
FIX ÚKLID DIALOG — WS PAYLOAD PASS-THROUGH
Když Martin po dávkách 14/15 zadal „navrhni úklid složky klienta X", Parťák správně vrátil hlavičku „📁 ÚKLID — <cesta> · Načteno N položek…" a backend obohatil WS payload o uklid_meta (snapshot + návrhy kategorií), ale tabulka s dropdownem se nevyrendrovala. Příčina: WS onmessage handler v partak-web/index.html připojoval na zprávu jen task_meta (dávka 5/v2.46), zatímco data.uklid_meta ignoroval — když pak appendMessage kontroloval m.uklid_meta, bylo undefined a renderUklidDialog se nezavolal. Fix: doplněna pass-through větev pro data.uklid_meta symetricky k task_meta. Backend, render funkce a endpointy beze změny.
20-05-2026 · v2.65
ZÁLOHA PŘED ÚKLIDEM — BACKUP + MOVE + MANIFEST (DÁVKA 15)
Úklid klientské OneDrive složky (dávka 14) dostal bezpečnostní vrstvu: před přesuny se vytvoří plná záloha. Kliknutí na PROVEĎ ÚKLID spustí čtyři kroky — (1) snapshot root-level položek (filtruje předchozí _backup-*), (2) vytvoření _backup-YYYYMMDD-HHMMSS/ uvnitř klientské složky a server-side copy celého snapshotu (Graph POST /copy s monitor URL pollingem, rekurzivní pro složky), (3) vytvoření kategoriálních podsložek, (4) atomický move per položka přes Graph PATCH parentReference. Když záloha nebo cíle selžou, přesuny NEZAČÍNAJÍ a backend vrátí stav v té fázi (phase: backup_failed / create_targets_failed). Per-item chyba v move fázi nezastavuje zbytek — záloha drží originály. Manifest obsahuje backup_path, folders_created a per-řádek from → to. Nové helpery v partak/onedrive.py: copy_file (s monitor pollingem + allowlist), move_file (allowlist-safe wrapper kolem move_item), create_folder (alias ensure_folder). Allowlist se kontroluje na zdroji i cíli každé operace. Mazání zálohy NENÍ součástí — odložené (Martin smaže ručně nebo přijde samostatná dávka). Legacy varianta bez zálohy zůstává na /api/uklid/execute_unsafe jen pro testy. Pravidla potvrzování v partak/CLAUDE.md sekce „Záloha před úklidem (dávka uklidsafe)".
20-05-2026 · v2.64
PŘEPÍNAČE ZDROJŮ KLIENTŮ + FIX MIX SOUBORŮ (DÁVKA 14b)
Tři související fixy kolem identifikace klienta: (1) Přepínače v Aktivním kontextu — checkbox „hledat klienty v OneDrive ROZPRACOVANÍ" (= Plocha/ROZPRACOVANÍ KLIENTI/) a „hledat klienty v GD _PARTAK" (= _PARTAK/klienti/). Default oba ON, persistent v active_context.json. Když je oba ON, pool jmen pro fuzzy match = sjednocení obou zdrojů; při remíze prioritu GD klient (kompletnější, má kontext.md). To řeší případ, kdy klient existuje v OneDrive ROZPRACOVANÍ (např. Herget Jiří), ale nemá zatím GD složku — Parťák ho dřív omylem zaktualizoval na sousedního Kukla Jiřího jen kvůli shodě na křestním jménu. (2) Aktivní OneDrive složka = JEN ta jedna konkrétní — backend dřív načítal soubory rekurzivně do hloubky 5, takže Herget Jiří/Herget Samuel Georg/ mixoval do jednoho seznamu. Teď čte jen root level + v hlavičce sekce ukazuje warning se seznamem podsložek. Pravidlo v CLAUDE.md, aby Parťák podsložky NESLUČOVAL. (3) search_in_folder striktní scope — Graph endpoint /me/drive/root:/PATH:/search() je v aktuální API jen "scope hint", index propustí soubory mimo PATH (typicky z jiného allowed prefixu — z archivu při hledání v ROZPRACOVANÍ). Striktní filtr: parent_path musí být přímo folder_path nebo jeho podsložka. Plus URL-decode _parent_path_from_ref — Graph vrací cesty percent-encoded (_ROZPRACOVAN%C3%8D_ → _ROZPRACOVANÝ_). V _format_search_results přibyl label „v podsložce: **X**", aby Martin viděl, kterému klientovi soubor patří.
20-05-2026 · v2.63
ÚKLID KLIENTSKÉ SLOŽKY — UI DIALOG (DÁVKA 14)
Předchozí rekurzivní analýza (dávka 13) byla pomalá a často nic nevracela. Dávka 14 ji nahrazuje jednoduchým UI dialogem ovládaným Martinem. Workflow: Martin řekne „navrhni úklid složky klienta X", Parťák vrátí token UKLID_FOLDER|<cesta>, backend vylistuje root level klientské složky (flat, žádná rekurze, žádný OCR) a u každé položky předvyplní pravděpodobnou kategorii podle názvu. V chatu se objeví tabulka s dropdownem u každého řádku — 10 kategorií: NEVÍM (default = ponechat), POJIŠTĚNÍ VOZIDLA, POJIŠTĚNÍ MAJETKU, ŽIVOTNÍ POJIŠTĚNÍ, POJIŠTĚNÍ PODNIKATELŮ, ÚVĚR, HYPOTÉKA, INVESTICE, POJISTNÁ UDÁLOST, OSTATNÍ. Martin si po projití klikne PROVEĎ ÚKLID → frontend zavolá /api/uklid/execute, backend vytvoří chybějící podsložky a atomicky přesune přes Graph PATCH /me/drive/items/{id} s parentReference — server-side move, žádné kopírování ani mazání. NEVÍM položky zůstanou na místě. Stará dávka 13 a alias ANALYZE_CLIENT_FOLDER| stále funguje (zpětná kompatibilita), ale interně se přepnula na nový UI flow. Smoke endpointy: /api/uklid/list a /api/uklid/execute. Pravidlo v partak/CLAUDE.md sekce „Úklid OneDrive klientských složek — UI dialog (dávka 14)".
20-05-2026 · v2.61
NÁPOVĚDA — AKTIVNÍ KONTEXT + DOVEDNOSTI
Dva panely v pravém sloupci — AKTIVNÍ KONTEXT a DOVEDNOSTI — dostaly ⓘ tlačítka a vlastní help sekce. help-aktivni-kontext popisuje, co je právě teď v Parťákově system promptu (📄 přiložené dokumenty s [GD]/[local] štítkem, 📁 OneDrive složka, 🎓 aktivní školení) — co s tím (✕ odebrání, /reset, persistence v active_context.json) a kdy se ozve „Prompt is too long" diagnostika. help-dovednosti vysvětluje skill systém: boot defaults (klient-dokumenty + plaud-zaznam) od v2.48, výčet co každý skill dělá (brand, spory-judikatura, web-search, …), auto-aktivace přes triggers v YAML hlavičce, 6 kB cap per skill. Filter banner ⓘ stále filtruje na jedinou sekci (v2.56), takže klik na ⓘ u panelu otevře přesně tu jednu nápovědu. Pouze frontend, žádný restart.
20-05-2026 · v2.60
ŠKOLENÍ — INDEX + TOKENY + FILTRACE (DÁVKA 12)
Implementace návrhu z dávky 11. Nový soubor partak/training_index.json (atomický zápis tmp + os.replace) drží metadata všech školení: {id, title, segment, institution, drive_path, date, archived, supersedes, superseded_by, tags}. Backend helpery: add_training_to_index, archive_training, find_trainings_by_segment, find_trainings_by_institution, find_trainings_for_comparison. Tokeny TRAINING_ADD|title|segment|institution|YYYY-MM-DD|drive_path|supersedes|tags a TRAINING_ARCHIVE|<id> — backend ukáže náhled a Martina se zeptá ano/ne. Při supersedes starý záznam automaticky dostane archived=true + superseded_by=<new_id>. Pravidlo 3 (nové / refresh / doplněk / sloučit) je v promptu — Parťák před TRAINING_ADD projde kandidáty z indexu a nabídne A/B/C/D. Filtrace: při dotazu „od ČPP / porovnej / školení k pojištění auta" backend match instituce/segment + fuzzy → vloží do system promptu sekci === TRAINING INDEX — kandidáti === s ID. Smoke: GET /api/training_index. JSON je v .gitignore (data, ne kód). One-time migrace 10 stávajících školení odložena — Parťák doplní postupně přes Martinovo potvrzení.
20-05-2026 · v2.59
AUDIT ŠKOLENÍ + NÁVRH STRUKTURY (DÁVKA 11)
Pouze textová dávka — audit toho, jak Parťák dnes pracuje se školeními, a návrh budoucí struktury. Stávající stav: modul partak/trainings.py + skill skoleni-poznamky, plochá Drive struktura _PARTAK/ŠKOLENÍ/<název>/ (10 záznamů), žádný index, žádná segmentace, žádný supersedes. Návrh do dávky 12: partak/training_index.json se segmenty (Pojištění / Investice / Úvěry / Ostatní / Archivované), fixed list institucí (ČPP, Kooperativa, Inbank, Investika, Wood & Company, Broker Trust, D.A.S., Generali ČP, KB pojišťovna, Allianz, UNIQA, Česká spořitelna, Edward Invest, Pillow, ČNB, Respect Finance, Jiná) a polem supersedes pro refresh. Pravidla pro přidání: nové / refresh existujícího / doplněk / archivace starého / sloučení — Parťák vždy navrhne A/B/C/D a počká na potvrzení, nikdy sám neklasifikuje. Detail v partak/CLAUDE.md sekce „Školení — návrh struktury". Žádný kód v této dávce, žádný restart není nutný.
20-05-2026 · v2.58
SWEEP VČEREJŠÍCH SCHŮZEK + INDEX ZÁPISŮ (DÁVKA 10)
Parťák teď ví, ke které schůzce už zápis existuje, a den po klientské schůzce bez zápisu pošle Telegram reminder. Nový soubor partak/client_notes_index.json drží [{client, event_id, meeting_date, note_path, created_at}]; každý úspěšný MEETING_SUMMARY upload do něj automaticky dopisuje (atomický zápis přes tmp + os.replace). Nový APScheduler job meetings_sweep_yesterday v 7:15 (před ranním briefingem) projde všechny včerejší Calendar eventy ze všech Martinových kalendářů, klasifikuje klientské (název s příjmením z _PARTAK/klienti/ NEBO externí účastník mimo domény respectfinance/martinhrna/brokertrust), porovná s indexem a pro chybějící zápisy pošle Telegram „📝 Včerejší schůzka bez zápisu …". State pending_meeting_followups[event_id] si zapamatuje kontext — když Martin pošle text/hlas/přílohu, backend mu zprávu prefixuje [FOLLOWUP — zápis ze včerejší schůzky] blokem (klient, datum, event_id) a Parťák z toho vyrobí MEETING_SUMMARY token. Po úspěšném uložení backend doplní event_id do indexu, takže další sweep ho přeskočí. Edge: klient neidentifikován → Parťák se zeptá tlačítkově (nehádá); slovo „přeskoč/skip/později" → followup zruší. Smoke test: POST /api/notes_sweep/run ručně, debug GET /api/notes_index.
20-05-2026 · v2.57
FIX MEETING_SUMMARY — TOLERANCE TOKENU + ZÁKAZ HALLUCINACE
Incident z logu 20.5.2026: Parťák v testu MEETING_SUMMARY vrátil zprávu „✓ Token MEETING_SUMMARY přijat. Backend nyní zobrazí náhled..." místo POUZE tokenu. Backend handler (startswith("MEETING_SUMMARY|")) ho nedetekoval — žádný náhled, žádný pending_meeting_summary. Když Martin napsal „ano", Parťák LLM si zhalucinoval, že upload proběhl, ačkoli soubor ve Drive vůbec nevznikl. Tři fixy: 1) Backend tolerance — detektor nyní hledá MEETING_SUMMARY| na samostatném řádku kdekoliv ve zprávě (jako LOG_WRITE). 2) Decode escape sekvencí — pokud text obsahuje literální \\n/\\t, převede je na skutečné newliny před uložením. 3) Posílení promptu (CLAUDE.md long + load_claude_md_short + composed sekce): explicitní zákaz preambule před tokenem („✓", „Token přijat", „Backend nyní…") + explicitní zákaz hallucinace „zápis byl uložen" před backendovým potvrzením. Reálné past-tense smí říct jen backend po skutečném API volání.
20-05-2026 · v2.56
NÁPOVĚDA — ⓘ FILTR + KOMPLETNÍ REVIZE
Když uživatel klepne na ⓘ ikonu u konkrétního UI prvku, nápověda se otevře a zobrazí jen tu jednu sekci (ne celou tříobrazovkovou nápovědu). Nahoře banner „▸ Zobrazena jen sekce: …" s tlačítkem ▸ ZOBRAZIT VŠE. Implementováno přes _setHelpFilter() — schová section[id^="help-"] kromě té zadané. Při zavření modalu se filter resetuje, takže další otevření z hlavního tlačítka ukáže opět vše. Plus kompletní revize obsahu: doplněna nová sekce help-kontext (3-zdrojová agregace + smart routing z dávky 7), aktualizována help-ukoly (checklist z v2.46), help-email (slovník navržených voleb v2.50 + zákaz future-tense slibů v2.51), help-onedrive (allowlist HLAVNI/ZALOHA z v2.52 + odpojení pro úsporu), help-pravidla (A/B na konci v2.48, výpovědní deadline v2.48, žádné fake past-tense v2.51). Pravidlo „při každé dávce revize Nápovědy" uloženo do paměti, aby se to už neopakovalo (z poslední revize chyběly dávky 6–9 v helpu).
20-05-2026 · v2.55
KLIENTSKÝ ZÁPIS Z JEDNÁNÍ (DÁVKA 9)
Nový skill: Parťák ze schůzky / hlasového diktátu / Plaud transkriptu vyrobí formální klientský zápis pro systém Beta a uloží do Google Drive _PARTAK/klientske-zaznamy/YYYY-MM-DD_prijmeni-jmeno.txt. Token MEETING_SUMMARY|<Příjmení Jméno>|<YYYY-MM-DD nebo prázdné>|<text> → backend ukáže náhled + ptá se „Uložit do Drive? (ano/ne)". Po „ano" upload + návrat webViewLink. Pevná struktura textu (bez markdownu): Datum, Klient, „Co se řešilo:", „Výsledek:", „Domluvené další kroky:", „Budoucí témata:", „Otevřené otázky:", patička s RESPECT FINANCE kontakty. Slug filename z příjmení+jména (bez diakritiky, kebab). Helpery get_or_create_partak_subfolder + upload_to_partak_subfolder + _client_filename_slug jsou rozšířitelné i pro další podsložky (např. _PARTAK/rekapitulace/). Pravidlo v CLAUDE.md i load_claude_md_short: před uložením povinný náhled, při shodě jmen klientů se zeptat tlačítkově, žádný markdown (jde do Bety).
20-05-2026 · v2.54
TŘI DENNÍ BRIEFINGY + SNAPSHOT NÁVAZNOST (DÁVKA 8)
Ranní briefing (7:30) rozšířen o dva nové sloty: 17:00 odpolední (hotové dnes, P1/P2 zbývá, deadline ≤ 3 dny) a 20:00 večerní (rekapitulace, nedokončené dnešní, úkoly bez termínu, nabídka přeplánování). Generická infrastrukturní změna: jeden helper send_daily_briefing(bot, slot) s atomickým zápisem do partak/daily_reports.jsonl (tmp + os.replace), retry pro Telegram (3 pokusy, exponenciální backoff). Snapshot obsahuje task_ids (Todoist), summary_text a telegram_message_id. Web navazování: když napíšeš „k těm úkolům z dneška", „přesuň zbylé na zítra", „z toho reportu", backend do system promptu vloží blok === NÁVAZNOST NA DENNÍ REPORT … se snapshot IDs — Parťák pracuje deterministicky s IDs, ne fuzzy podle textu. Edge cases: Todoist down → report poslán s poznámkou „Todoist nedostupný"; Telegram send fail → 3 retry; úkoly bez termínu se objeví ve večerním reportu. Smoke test: POST /api/briefing/run {"slot":"afternoon"} spustí ručně. .gitignore doplněn o partak/daily_reports.jsonl.
20-05-2026 · v2.53
KLIENTSKÝ KONTEXT — 3 ZDROJE + SMART ROUTING (DÁVKA 7)
Při klientském dotazu Parťák nově konzistentně bere v úvahu tři zdroje: Todoist (úkoly s prefixem/labelem klienta z dávky 5), Google Calendar (schůzky ±14 / +60 dní) a OneDrive klientskou složku (aktivní složka nebo search v Plocha/ROZPRACOVANÍ KLIENTI; archiv jen na explicitní žádost). Backend do system promptu vkládá blok === POUŽITÉ ZDROJE PRO TENTO DOTAZ === s výčtem co prošlo a co ne — Parťák to v odpovědi na konci uvádí řádkem „Zdroje: Todoist (5 úkolů), Calendar (2 schůzky), OneDrive [složka], … (model: Sonnet 4.6 / Haiku 4.5)". Smart routing rozšířen: pro klientské poradenské dotazy (active klient + slova „porovn / vyhodno / doporuč / nejvhodnější / nejlepší / vyber / rozhodni / která pojišťovna / který produkt / který fond") backend forcsne Sonnet 4.6. Usage log: log_usage nyní zapisuje i pole context_sources a active_client — pro pozdější analýzu (kde se utrácí). Default zůstává Haiku 4.5, ručně přepneš ⚡ AUTO/HAIKU/SONNET v hlavičce.
20-05-2026 · v2.52
USAGE ÚSPORA — ODPOJENÍ ONEDRIVE + ARCHIV NA VYŽÁDÁNÍ
Bug: Martin s Parťákem nad klientem řešil novou věc, která nesouvisela s historií dokumentů na OneDrive. OneDrive složka klienta zůstala připojená a Parťák do system promptu vlékl rekurzivně její obsah (~10 souborů × 5 MB) — na jednom dotazu se vyčerpalo hodně usage. Tři úpravy: 1) Hlavní složka rozpracovaných přejmenována v ONEDRIVE_ALLOWED_PATHS.txt z generického SLOZKA_PLNA_PRAVA_* na HLAVNI_ROZPRACOVANI (= Plocha/ROZPRACOVANÍ KLIENTI) a ZALOHA_ARCHIV (= Dokumenty/___KLIENTI___). 2) Archiv se prohledává jen na explicitní žádost — když Martin použije slovo „archiv / historie / starou smlouvu / dříve / loni / vyřízený", nebo když v ROZPRACOVANI nic není (fallback). Předtím Parťák tlouk obojí pokaždé. 3) Nový token UNSET_ONEDRIVE_FOLDER + pravidlo v promptu (Haiku i Sonnet): když Parťák vidí v kontextu aktivní OneDrive složku a Martin přejde na nesouvisející věc, NABÍDNE A/B/C (pokračovat / odpojit / připojit jinou). Když Martin sám řekne „odpoj OneDrive", token jde rovnou bez ptaní. WS event onedrive_folder_changed obnoví aktivní kontext panel ve frontendu.
20-05-2026 · v2.51
ŽÁDNÉ FUTURE-TENSE SLIBY + A/B PO TRANSFORMACI
Reálný bug 20.5.2026: Martin požádal o přepis konceptu do tykání. Parťák napsal „přepíšu a rovnou uložím jako koncept", přepis udělal, ale token EMAIL_DRAFT nezavolal — koncept se neuložil. Slib bez splnění = stejně škodlivé jako fakeované „uložil jsem". Pravidlo do CLAUDE.md i load_claude_md_short: NIKDY future-tense „uložím / pošlu / udělám" bez okamžitého splnění. Buď akci provedeš tokenem ve STEJNÉ zprávě (backend ji potvrdí), nebo nabídneš A/B a počkáš. Třetí varianta neexistuje. Plus speciální pravidlo pro transformaci textu (přepis tykání/vykání, oprava jména, oprava diakritiky, překlad): vrátit nový text + povinné A/B (A) Uložit jako koncept | B) Ještě upravit). Martin si chce transformaci zkontrolovat, než zmizí do schránky.
20-05-2026 · v2.50
SLOVNÍK NAVRŽENÝCH E-MAILOVÝCH VOLEB
Bug s dvojím potvrzováním: Parťák ve volbě napsal A) Pošli e-mail Plevovi, Martin klikl v očekávání přípravy návrhu — místo toho mu naskočilo Potvrdit odeslání? (ano/ne), protože slovo „pošli" v promptu Parťák vyhodnotil jako STAV 3 (ODESLÁNO) a vrátil token EMAIL_SEND. Až po další volbě se objevilo „Uložit jako koncept / Upravit text" — matoucí dvojí krok k téže akci. Pravidlo (v CLAUDE.md, load_claude_md_short i composed sekci promptu): v A/B tlačítkách a ⟦NÁVRHY⟧ blocích **pro krok 1/2 výhradně** „napsat/sepsat/připravit/vytvořit/navrhnout". Slova „pošli/odešli/send" jsou **vyhrazená jen pro stav 3 ODESLÁNO** — sám je Parťák v navržených tlačítkách nenabízí. Stav 3 přichází buď po explicitním Martinově „odešli to teď" v chatu, nebo jako ANO/NE reakce na backendové potvrzení po EMAIL_SEND tokenu. Stav 3 trigger words v promptu zpřesněny (typické fráze, pojistka „při pochybnostech NEZAKLÁDEJ EMAIL_SEND, zeptej se tlačítkově").
20-05-2026 · v2.49
KALIBRACE USAGE — PROMPT CACHING
Parťák dosud počítal input tokeny plnou cenou, ale Claude Code CLI používá prompt caching (5 min TTL) — system prompt + CLAUDE.md stojí 10 % normální ceny. Dnešní cvičení: Parťák hlásil $4.79 / $5 = 96 % z 5h okna, Anthropic dashboard ukazoval 47 % celkového Max plánu (z toho ~95 % je Claude Code). Zaveden cache modeling: cost = (in_cached × 0.1 + in_fresh) × price_input + out × price_output, default PARTAK_CACHE_HIT_RATIO=0.75 (env-tunable). Po retro přepočtu dnešních záznamů (17 řádků v usage.jsonl) klesl dnešní cost $5.74 → $1.92 (faktor 0.335×). Pokud realita ukáže, že je odhad pořád příliš vysoký nebo nízký, ladit přes PARTAK_CACHE_HIT_RATIO v .env.
20-05-2026 · v2.48
VÝPOVĚDNÍ DEADLINE POJISTKY + POSÍLENÍ A/B TLAČÍTEK
Zpracován improvement log (3 záznamy) + skills boot defaults. 0) Skills defaults po startu service — Parťák při bootu vždy začíná s aktivními klient-dokumenty + plaud-zaznam, ostatní vypnuto. Martin si může během session zapnout cokoliv navíc (v UI panelu), ale restart vrátí stav na default — žádné překvapení z minulé session. Defaults žijí jako DEFAULT_ACTIVE_SKILLS v partak/skills.py, reset volá main() v partak-bot.py před spuštěním web/Telegram smyček. 1) Výpovědní deadline pojistné smlouvy — Parťák nyní vždy spontánně počítá deadline = výročí − 42 dní (§ 2807 OZ, počítají se všechny kalendářní dny vč. víkendů a svátků), když uživatel zmíní výročí smlouvy, přechod od jiné pojišťovny, výpověď nebo „převést ke mně". Výstup vždy obsahuje datum výročí, datum deadline doručení (s názvem dne), zbývá X dní a status: „stíháme v klidu" (>14 dní), ⚠ „těsně" (0–14), 🛑 „nestíháme — až k příštímu výročí" (≤0). Příklad: výročí 10.7.2026 → deadline 29.5.2026 → zbývá 9 dní → ⚠ těsně, jednat hned přes online portál nebo e-mail. 2) A/B tlačítka na konci zprávy — Parťák občas zapomínal vykreslit A/B/C volby pod otázkou, frontend regex je pak nezachytil a Martin musel psát volbu ručně. Před-send sanity check v promptu (Haiku i Sonnet) říká: před vrácením zprávy si polož „Pokládám otázku nebo nabízím alternativní akce?" — pokud ano, A/B/C blok MUSÍ být na posledních řádcích, bez markdownu, každý klíč na vlastním řádku. Pravidlo posíleno i v dlouhém CLAUDE.md, abych se k tomu při příštím rollovi vrátil.
20-05-2026 · v2.47
SANITIZACE NEVIDITELNÝCH ZNAKŮ + JASNĚJŠÍ WARNINGY
Dva související fixy v jednom: 1) Variation Selector-16 (U+FE0F, neviditelný emoji modifikátor), ZWJ, BOM, bidi marks a další neviditelné znaky se nyní tiše odstraňují PŘED detektorem cizích písem. Předtím detector hlásil falešný poplach na každé emoji v textu, což zmátl celou kaskádu — Martin nevěděl, jestli koncept e-mailu uložen byl, nebo ne. Sanitizace v sanitize_invisible_chars dropne 23 specifických kódových bodů, které ničí PDF/email rendering ale v lokálním kontextu nenesou význam. 2) Když cizí znaky opravdu jsou (CJK, arabské, hindské halucinace), warning teď výslovně sdělí STAV: „NEPROVEDL JSEM koncept/úkol/událost — tato zpráva je jen text v chatu" nebo „token akce ve zprávě je, ale provede se až po tvém ANO". Plus A/B/C tlačítka co dál (regenerovat / opravit ručně / zahodit). 3) Při Prompt is too long z Claude CLI Parťák místo generického „chyba (1)" zobrazí diagnostiku (kolik znaků, kolik zpráv v paměti, kolik dokumentů, který model) a A/B/C: reset paměti / přepnout na Sonnet 1M / odpojit OneDrive složku.
20-05-2026 · v2.46
TODOIST CHECKLIST POD VÝPISEM ÚKOLŮ
Když Parťák ve webu vypíše úkoly s ID v hranatých závorkách (typicky „Co mám u klienta Novák?", „Co mám dnes?"), pod zprávou se teď vykreslí checklist — zaškrtni hotové a klikni DOKONČIT VYBRANÉ. Backend volá Todoist /api/v1/tasks/<id>/close per ID a vrátí per-řádek výsledek: zelená „HOTOVO", zlatá „JIŽ DOKONČENO" (uzavřeno jinde, např. v appce), červená „CHYBA" s tooltipem. Vyžaduje dávku 5 — úkoly musí mít prefix nebo label aby šly najít zpětně. Backend: nový endpoint POST /api/todoist/complete, helper extract_todoist_task_meta regexem najde ID v odpovědi a WS payload obohatí o task_meta. Telegram dostává nadále jen plain text — inline keyboard pro něj je samostatná dávka. Frontend: vanilla JS render, žádný framework. Cache-Control: no-store na / + meta build marker v2.46.
20-05-2026 · v2.45
ZNAČENÍ ÚKOLŮ PODLE KLIENTA
Úkoly v Todoistu jsou teď zpětně dohledatelné podle klienta — připravuje to checklist v dávce 6. Konvence: název úkolu s prefixem [Klient: Příjmení Jméno] a label klient-prijmeni-jmeno (kebab, bez diakritiky). Token TODOIST_ADD má nové 6. pole `klient` — když ho Parťák vyplní kanonickým jménem z _PARTAK/klienti/, backend automaticky předřadí prefix a přiřadí label. Při hledání („Co mám u klienta Novák?") find_todoist_tasks_for_client matchuje deterministicky přes prefix nebo label, fallback je původní fuzzy match příjmení — staré úkoly bez konvence se najdou dál. Když z věty není klient jasný, Parťák se ZEPTÁ tlačítkově (A: přiřadit / B: jiný klient / C: bez klienta) místo aby hádal. Pravidlo doplněno do partak/CLAUDE.md a do sekce ÚKOLY A TODOIST v Nápovědě. Vyžádaný restart partak.service.
20-05-2026 · v2.44
NÁPOVĚDA K PŘÍLOHÁM — DÁVKA 2
Sekce PŘÍLOHY v nápovědě rozšířena o reálné porovnání Telegram vs. web. Telegram bot má aktuálně handlery jen pro text a hlas — fotku ani dokument nepřijímá (předchozí znění to slibovalo nepravdivě). Web umí PDF/DOCX/XLSX/TXT s extrakcí textu, obrázky uloží bez čtení obsahu, OCR funguje jen pro PDF v OneDrive. Doplněna tabulka formátů, limit 25 MB, popis jak Parťák s přílohou dál pracuje (Drive _PARTAK, system prompt blok === PŘILOŽENÝ DOKUMENT ===, Plaud detekce). Přílohy nejdou do Claude CLI jako soubory — vždy jen jako extrahovaný text přes stdin. Sjednocení handlerů pro Telegram (foto/dokument) odloženo na příští dávku.
19-05-2026 · v2.43
STRIKTNÍ STAVY E-MAILŮ, A/B TLAČÍTKA, AUTO-MODEL
Šest navazujících oprav z odpoledne 19. 5.: 1) e-mailové koncepty se konečně ukládají do IMAP složky „Koncepty" (ne „drafts"), striktní rozlišování stavů (návrh / koncept / odesláno). 2) A/B tlačítka se v UI vykreslují pod zprávou a nezasahují do bloku ⟦NÁVRHY⟧. 3) Po výběru klienta v kartě klienta se viditelně potvrdí výběr. 4) Nové tlačítko AKTUALIZOVAT ÚKOLY Z CHATU + proaktivní sledování splnění (Parťák si všimne věty jako „už dodal" a nabídne TODOIST_COMPLETE). 5) Claude CLI timeout 300 → 1800 s, auto-režim přepíná na Sonnet pro PDF/rekapitulace. 6) Striktnější parsing A/B (kombinovaný klíč „A/B)" je zakázaný) + detektor halucinovaných cizích znaků (CJK/arabština/hindština uvnitř českého textu).
19-05-2026 · v2.42
OPRAVA POPISU TELEGRAM VS. WEB
Předchozí znění nápovědy tvrdilo, že Telegram a web sdílejí historii — což platí jen pro serverovou paměť (history.json, kterou používá Claude). Vizuální historie v UI je per kanál: web zobrazuje jen webové zprávy (localStorage), Telegram jen Telegramové. Tlačítko ⟲ / příkaz /reset ve webu smaže lokální zobrazení i celý server-side history.json — Parťák tím zapomene i to, co padlo v Telegramu. Pro pouhou změnu tématu bez ztráty paměti řekni slovy, např. „pojďme na nové téma, zapomeň na ten e-mail".
19-05-2026 · v2.41
DOPLNĚNÍ NÁPOVĚDY A PRAVIDEL TRANSPARENTNOSTI
Nová sekce Nápověda v menu s deseti tématy (úkoly, kalendář, e-maily, OneDrive, školení, Telegram vs. web, přílohy, briefing, hlas, pravidla zadávání) — u každého popis funkce, příklady a limity. Současně doplněna pravidla pro úplnost výpisů (kolik nalezeno/zobrazeno/rozsah), transparentnost zdrojů (které, za jaké období, co ne) a jednoznačné rozlišení stavů e-mailu (návrh / koncept / odesláno). Backend zároveň rozšířen o tokeny MOVE_FILE_SKOLENI a UPDATE_KONTEXT. Dávka 1.
19-05-2026 · v2.4
DVOUDENNÍ ITERACE — VOICE, USAGE, ONEDRIVE
::: rekonstruováno z gitu — verze před spuštěním Novinek :::
Velký skok funkcionality, který byl v provozu, ale ještě bez vlastní novinky: hlasové zprávy přes OpenAI Whisper (Telegram VOICE + webový mikrofon přes MediaRecorder), trackování token usage s 5h a týdenním Claude Max rozpočtem (modal pod ikonkou Usage), automatické párování klienta podle příjmení s tlačítkovou disambiguací při shodě, async call_claude (neblokuje event loop), allowed_tools pro skill web-search, příprava OCR pipeline pro OneDrive. Web: drag-and-drop upload, mikrofon, dark/light režim, brand barvy RESPECT FINANCE (navy + gold), responzivní layout s podporou Samsung Fold. OneDrive: device-flow auth (Graph PowerShell client, tenant consumers) + helpery list/download/upload. Toto je verze, která dlouho seděla na přihlašovací obrazovce jako v2.4 — od ní se nyní čísluje po setinách.
17-05-2026 · v2.3
HTTPS PŘES NGINX + LET'S ENCRYPT
::: rekonstruováno z gitu :::
Produkční nginx config zachycen do repa po nasazení certbotu — SSL certifikát platný do 14. 8. 2026, auto-renew přes certbot.timer. Reverse proxy směruje provoz na FastAPI 127.0.0.1:8000, HTTP → HTTPS redirect, WebSocket upgrade pro /ws. README aktualizován s healthcheck/renew příkazy.
17-05-2026 · v2.2
README PRO GITHUB STRÁNKU
::: rekonstruováno z gitu :::
Původní README byl interní provozní příručka s odhalenými prefixy tokenů. Přepsáno jako popis repa pro návštěvníka GitHub stránky: „Co Parťák umí", setup flow (clone, venv, .env, OAuth, systemd), konfigurační tabulka bez secrets, aktualizovaná roadmapa, sekce Bezpečnost popisující strategii .gitignore + .env.
17-05-2026 · v2.1
OPRAVA GOOGLE SCOPES
::: rekonstruováno z gitu :::
První produkční oprava: bot požadoval full calendar scope, ale token.json byl získán pro calendar.readonly + calendar.events. Google při refreshi vracel invalid_scope a celý Drive _PARTAK flow byl mrtvý. Sjednoceno na calendar.events (stačí na read+write) bez nutnosti znovu autorizovat.
17-05-2026 · v2.0
PARŤÁK NA GITHUBU — INITIAL COMMIT
::: rekonstruováno z gitu — nultá publikovaná verze :::
První commit do soukromého repa martinhrna/Partak. Telegram bot @partak_mh_bot + FastAPI web na portu 8000, integrace Google Calendar / Todoist / OneDrive, hlasové zprávy přes OpenAI Whisper, ranní briefing v 7:30. Všechny secrety přesunuté z hardcoded konstant do .env (gitignored), šablona v .env.example. Číslo verze v2.0 odpovídá stavu, který byl tehdy stabilní v provozu — dřívější iterace (v0.x, v1.x) běžely jen lokálně před založením repa.