skill and develop
This commit is contained in:
@@ -2173,3 +2173,157 @@ Běh 5 modelů s gate=3:
|
||||
**Co zbývá:** Zápis do `decisions.md` (návrh připraven, čeká na formulaci autora — ruší dosavadní rámování „duplicitní texty povoleny by design"). Behaviorální ověření dělá uživatel v chatu.
|
||||
|
||||
**Jak vrátit zpět:** Repo `git revert 61e7068` + `rsync` na server. Změna je čistě logika CLI (žádná migrace DB), revert je bezpečný.
|
||||
|
||||
## 2026-07-15 — sync skillů ze serveru: bookmark autonomně upravený Dreamem
|
||||
|
||||
**Cíl:** Uživatel požádal o obecný sync skillů ze serveru do repa (bez upřesnění, který). Podle pravidla v `CLAUDE.md` („server je zdroj pravdy") jsem prošel checksumem všechny lokální skilly proti serveru před jakýmkoli přepisem.
|
||||
|
||||
**Co jsem zkusil:** `rsync --dry-run --checksum --itemize-changes` pro každý skill (`article`, `bash`, `bookmark`, `deep-research`, `detach`, `grill-me`, `keep`, `llm-wiki`, `note`, `plan`, `python`; `remind` ověřen zvlášť plným stažením a `diff -rq`).
|
||||
|
||||
**Co fungovalo a proč:** `remind` beze změny (server == lokál, potvrzuje předchozí záznam). `detach` a `llm-wiki` hlásily rozdíl jen v mtime (`.f..t......`, bez `c`/`s`) → obsah identický, ignorováno. `bookmark/SKILL.md` měl skutečný obsahový rozdíl (`>fcst......`) — server má autonomní úpravu (pravděpodobně Dream): (1) přehozené pořadí ve formátu výpisu — `<description>` teď před `[<tags>]`, ne za nimi; (2) nová sekce „Known limitations" (chybí `edit`/`update`, `delete` je jen soft-delete, weby za Cloudflare nejdou auto-fetchnout). Stáhl jsem přesně tuto verzi do `skills/bookmark/SKILL.md`, ověřeno `diff` na 100% shodu.
|
||||
|
||||
**Co zbývá:** Server != repo u `keep` (SKILL.md) — dry-run to ukázal, ale nebylo součástí zadání, needitoval jsem. Zvážit v příští session. Drobná nekonzistence, kterou sync odhalil a nechal beze změny: příklad výpisu na řádku `#3 [hackaday.com](...) [linux, strace] — lepší strace` v `bookmark/SKILL.md` neodpovídá nově popsanému formátu (description by měl být před tagy) — je to takto i na serveru, nešlo o repo vs. server rozdíl, takže jsem to nepravil bez zeptání.
|
||||
|
||||
## 2026-07-15 — compact-memory-auto-daily: diagnóza "podivných hlášek" na Telegramu + migrace na crontab s čerstvou session
|
||||
|
||||
**Cíl:** Uživatel hlásil podivné/nesmyslné hlášky na Telegramu z posledních dní; v noci má běžet `compact-memory` z cronu, ale něco je špatně. Nejdřív jen prozkoumat (bez zásahu), pak — po potvrzení, že denní doručení je záměr — opravit kontaminaci kontextu.
|
||||
|
||||
**Diagnóza (nainstalovaný `nanobot-ai` v0.2.2):** Cron job `compact-memory-auto-daily` (`jobs.json`, id `69484aa6`) byl `agent_turn` s `payload.session_key = "telegram:8826147089"` — **stejná trvalá session jako běžný Telegram chat** (potvrzeno v `sessions/telegram_8826147089.jsonl`: 75/77 zpráv obyčejná konverzace, jen 2 cron marker). `is_bound_cron_job`/`run_bound_cron_job` (nahrazuje starý "hardcoded reminder preamble + evaluate_response" mechanismus popsaný výše v knowledge.md pro starší verzi) doručuje odpověď **bez jakéhokoli gatingu** — cokoli agent v tahu napíše, jde přímo do Telegramu. Navíc `origin_delivery_context` (kam se doručí) je **nezávislé** na `session_key` (do jaké session/historie se zapíše) — to je klíč k opravě. 2026-07-14 se agent v cron tahu spletl a pokusil se spustit neexistující `scripts/compact_memory.py` (skill ho nikdy neměl mít), napsal do chatu zmatenou zprávu, uživatel ho opravil ve stejné session — a protože session je trvalá, ten omyl kontaminoval i další noci (přesně text, co uživatel citoval jako "podivnou hlášku", je z nočního běhu 2026-07-15 02:00).
|
||||
|
||||
**Řešení (schváleno přes plan mode):** Migrace na vzor `/remind`/`detach` — systémový crontab + samostatný skript mimo nanobot `cron/jobs.json`, s **unikátní `session_key` per běh** (nikdy znovu použitý → nikdy žádná akumulace historie), doručení přímo přes Telegram Bot API (mimo nanobot channel pipeline).
|
||||
|
||||
**Co jsem udělal:**
|
||||
- Stáhl `skills/compact-memory/SKILL.md` ze serveru do repa poprvé (skill dosud nebyl trackovaný lokálně) + přidal větu "This skill has no accompanying script — every step below is performed by you, the agent..." — cílená prevence přesně té 07-14 halucinace.
|
||||
- Nový `skills/compact-memory/scripts/compact_memory_auto.py` (PEP 723, vzor `tasks-daemon.py`/`remind_send.py`): `Nanobot.from_config().run(GOAL, session_key=f"compact-memory-auto:{timestamp}")` s 10min timeoutem, výsledek poslán přímo `urllib` na Telegram Bot API (token/chat_id z `config.json`, stejný `_telegram_config()` vzor).
|
||||
- Nasazeno `rsync -av skills/compact-memory/` (celý adresář), owner `nanobot:nanobot` ✓.
|
||||
- Přidán řádek do system crontabu (`0 2 * * * uv run .../compact_memory_auto.py >> log/compact_memory_auto_cron.log`), stávající 3 řádky (remind/wiki-compile/note-compile) nedotčené.
|
||||
- Odebrán job `69484aa6` z `jobs.json` (in-place Python edit, bez restartu — zbylé 3 joby beze změny).
|
||||
- Opravena zastaralá věta v serverovém `memory/MEMORY.md` (psala, že job běží v Telegram session — teď už neplatí).
|
||||
|
||||
**Co fungovalo a proč:** Ruční test skriptu na serveru proběhl čistě (exit 0): reálný audit MEMORY.md (32 řádků), skutečně smazal 1 duplicitu a sloučil 2 dvojice, zapsal zálohu do `backup/2026-07-15_0541_memory.backup.md` i do `log/memory-clean.log`, report doručen do Telegramu. Ověřeno, že vznikla **nová izolovaná** session `sessions/compact-memory-auto_20260715-054147.jsonl` a `telegram_8826147089.jsonl` zůstal nedotčený (78 řádků beze změny) — potvrzuje, že `session_key` a `origin_channel`/`origin_chat_id` jsou v `nanobot-ai` 0.2.2 opravdu nezávislé, přesně jak ukázala explorace zdrojáku.
|
||||
|
||||
**Co zbývá:** Počkat na první reálný noční běh (příští 2:00) a ověřit stejné chování automaticky. Navrhnout uživateli větu do `decisions.md` (čeká na formulaci autora): "Nanobot `agent_turn` cron joby pro neinteraktivní/background úlohy vždy mimo `jobs.json`, přes systémový crontab + přímé `Nanobot.run()` s per-run unikátní `session_key` — stejný vzor jako `/remind` a `detach`." Zvážit doplnění knowledge.md o `is_bound_cron_job`/`origin_delivery_context` mechanismus 0.2.2 (nahrazuje starší popsaný "hardcoded preamble + evaluate_response" — ten je teď verzí-specifický/zastaralý).
|
||||
|
||||
**Jak vrátit zpět:** V `jobs.json` přidat job `69484aa6` zpět (zálohovaný obsah je v git historii commitu před touto změnou), odebrat řádek z crontabu (`crontab -e`), smazat `skills/compact-memory/scripts/compact_memory_auto.py`. Migrace `MEMORY.md` (smazaná duplicita, sloučené bullet) je nedestruktivní — plný snapshot je v `backup/2026-07-15_0541_memory.backup.md` na serveru.
|
||||
|
||||
## 2026-07-22 — nový skill `project` (nahrazen netrackovaný server-side skill, migrace `radio-1`)
|
||||
|
||||
**Cíl:** Uživatel chtěl nový skill `project`, analogii Claude.ai "Projects" — pojmenované dlouhodobé pracovní kontexty s vlastním promptem, historií a artefakty. Plánováno v plan mode: prozkoumáno 13 existujících skillů + `knowledge.md` pro konvence (`description` = jen routing signál s "Triggers on:", cesty workspace-relativní, žádný sticky session-state u nanobota). Uživatel v review odmítl jmenovat konkrétní jiné skilly v description/těle (odlišení jen funkčně) a trval na tom, že skill smí zakládat nový projekt jen po explicitním potvrzení, nikdy automaticky. Přidán čtvrtý soubor `state.md` (živý, přepisovaný dokument "kde to teď je") vedle append-only `memory.md`.
|
||||
|
||||
**Co jsem zkusil:** Před nasazením (pravidlo "server je zdroj pravdy") jsem ověřil `~/.nanobot/workspace/skills/project/` na serveru — **a objevil tam už existující, plně funkční `project` skill, nikde v tomto repu netrackovaný** (žádná zmínka v `history.md`/`knowledge.md`/`decisions.md`, žádná git historie k `skills/project/`). Starý design: plochý soubor `projects/<slug>.md` s YAML frontmatterem (`status: active|paused|done`, `priority: high|medium|low`), tělo se sekcemi `## Poznámky`/`## Další krok`, CLI backend `scripts/project.py` (add/list/show/status), `switch` ukládající aktivní projekt do `my(action="set", key="project_context", ...)` scratchpad nástroje, frontmatter měl i `aliases: [proj]`. Existovala reálná data: `projects/radio-1.md` (založeno 2026-06-09, projekt na stříhání audio streamu Radia 1).
|
||||
|
||||
**Co fungovalo a proč:** Zastavil jsem se a nechal uživatele rozhodnout (AskUserQuestion) — odpověď: přepsat novým designem, `radio-1` migrovat na `radio1`. Provedeno: `rsync -av --delete skills/project/ nanobot@...` (smazal starý `scripts/project.py`, nahradil `SKILL.md` novým), obsah `radio-1.md` ručně rozparsován do nového formátu — dated bullet z `## Poznámky` → `projects/radio1/memory.md`, cíl+stav+priorita syntetizovány do `projects/radio1/state.md`, `prompt.md` prázdný (nic k migraci), `artifacts/` prázdný adresář — nahráno přes `rsync` do `~/.nanobot/workspace/projects/radio1/`, starý `projects/radio-1.md` smazán. Ověřeno vlastnictví `nanobot:nanobot` u všech nových souborů/adresářů.
|
||||
|
||||
**Co zbývá:** Ověřit na živém chatu, že skill funguje end-to-end (aktivace, switch, ukončení, listing, zápis do `memory.md`/`state.md`) — zatím jen nasazeno a data migrována, ne vyzkoušeno v konverzaci. Zvážit zápis do `knowledge.md`: nanobot má `my(action="set"/"get", key=..., value=...)` scratchpad nástroj pro perzistentní stav napříč tahy (starý skill ho používal pro `project_context`) — objeveno náhodou, nebylo součástí dřívějšího průzkumu "nanobot nemá sticky session-state"; stálo by za ověření zdrojáku, jak přesně `my` funguje (per-session, nebo globální — starý skill sám poznamenává "lost on restart", žádnou zmínku o per-session scoping). Navrhnout `decisions.md` zápis (viz níže, čeká na souhlas autora).
|
||||
|
||||
**Jak vrátit zpět:** Stará verze skillu (`SKILL.md` + `scripts/project.py`) je stažená v `tmp/server-project-check/` (negitované, lokální). Obsah `radio-1.md` je beze změny zachycen ve stejném adresáři přes `git show`/`cat ~/.nanobot/workspace/projects/radio1/memory.md`+`state.md` (sloučit zpět ručně). Žádná záloha stažené staré verze v gitu — je jen v `tmp/`, které je gitignored; pokud má mít trvalou stopu, je potřeba ji dodatečně zkopírovat mimo `tmp/`.
|
||||
|
||||
## 2026-07-22 — nový projekt `life` založen na serveru
|
||||
|
||||
**Cíl:** Uživatel chtěl na serveru přes skill `project` založit dlouhodobý projekt `life` — osobní průvodce/deníček k udržitelnému životnímu stylu (cvičení, běh, jídelní návyky, skloubení s rodinou a dítětem), kam si bude průběžně zapisovat úvahy. Primární cíl projektu: změna váhy + dobrá kondice.
|
||||
|
||||
**Co jsem zkusil:** Ověřil stav serveru (skill `project` nasazený a identický s repem, existoval jen `radio1`, žádný `life`). V plan mode přes AskUserQuestion vyjasnil scope. Rozhodnutí uživatele: jídlo řešit jako návyky (večerní jedení, sladké), NE počítat kalorie ani zapisovat jídlo (kalorické tabulky má stranou); oblasti navíc = alkohol + protahování/mobilita (dělá po běhu, chce integrovat pravidelněji); cvičení (vlastní váha/kettlebell) i běh zatím nepravidelné; pokrok sleduje denním vážením + vnímanou kondicí a bolestmi; role agenta = průvodce/deníček, přístup se bude zpřesňovat; do `artifacts/` jdou prohřešky a reálné překážky.
|
||||
|
||||
**Co fungovalo a proč:** Připravil 4 soubory lokálně ve scratchpadu a nasadil `rsync -av scratchpad/life/ nanobot@nanobot.hell:.../projects/life/` — `prompt.md` (kontext + oblasti + kam co zapisovat + „první session" checklist), `state.md` (skeleton výchozího stavu/cílů), `memory.md` (první záznam se založením), `artifacts/prekazky-a-prohresky.md` (prázdný starter). Ověřeno: strom 4 soubory + adresář, vše `nanobot:nanobot`. Skilly/projekty se čtou bez restartu služby.
|
||||
|
||||
**Co zbývá:** Vyzkoušet v živém chatu aktivaci („projekt life" → načtení `prompt.md`). Výchozí čísla (aktuální váha, časový rozpočet, cílová váha/horizont) doplní uživatel v první session — záměrně nevyplněno (skill: nefabrikovat obsah). Projekt žije jen na serveru, do repa se necommituje (stejně jako `radio1`).
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-25 — compact-memory: zkrácení nočního reportu (jeden řádek při nule) + append-only fix logu
|
||||
|
||||
**Cíl:** Noční běh `compact-memory` (crontab `0 2 * * *`) posílal na Telegram 700–1300 znaků i v nocích, kdy nic nesmazal. Uživatel: při nule stačí věta „nebylo nic k smazání", při reálné změně stačí *co* bylo smazáno/sloučeno a *proč*. Frekvence zůstává.
|
||||
|
||||
**Diagnóza:** Server = repo (`diff` čistý, žádný drift). Poslední 4 noci (07-22…07-25) všechny `deleted: 0, merged: 0` a přesto narativní walkthrough sekce po sekci + statistický code block + „Borderline kept". Čtyři zdroje, všechny předepsané:
|
||||
|
||||
1. `SKILL.md` Auto mode krok 4 — „Print a detailed list of changes."
|
||||
2. `SKILL.md` Output format → Auto mode report — vynucený blok `deleted/merged/unchanged` + sekce `Details:`, kterou agent vyplnil i při nule.
|
||||
3. `SKILL.md` Rules — „list the borderline items you kept in the report" (vyrobilo dvoubodový seznam 07-25).
|
||||
4. `GOAL` ve skriptu — žádné omezení výstupu + formulace jako uživatelský příkaz → agent si to vyložil jako explicitní vyvolání („protože jsi explicitně vyvolal skill, dělal jsem full audit" — 3 ze 4 nocí).
|
||||
|
||||
**Co jsem zkusil:** `SKILL.md` Auto mode přepsán na dvě výstupní cesty (nula → jedna věta, stop, žádná záloha ani log; změna → záloha + řádek per položku s kategorií a důvodem), zrušeno hlášení borderline-kept a přidán explicitní zákaz narace auditu, `Output format` má teď dva auto tvary. `GOAL` doplněn o „unattended nightly run, not an interactive request" + převeden z implicitní konkatenace literálů na triple-quoted string (precedens `SCHEMA = """…"""` v `bookmark.py`/`db.py`). Interactive mode nedotčen. Nasazeno `rsync -av skills/compact-memory/` (celý adresář), owner ✓, bez restartu.
|
||||
|
||||
**Co fungovalo a proč:** Tři reálné běhy na serveru (exit 0):
|
||||
|
||||
- **Nula změn:** doručeno 56 znaků (`Memory compact: nothing to delete (MEMORY.md, 28 lines).`), žádná nová záloha, log nedotčen, `MEMORY.md` beze změny.
|
||||
- **Se změnou** (reverzibilní test — vložena zjevně ephemeral odrážka do `MEMORY.md`): doručeno 165–207 znaků = hlavička + jeden řádek `[ephemeral] "…" — důvod`, smazána právě ta odrážka, vznikla záloha `backup/2026-07-25_0640_memory.backup.md`, `MEMORY.md` se vrátil do původního stavu (`diff` vs. pretest kopie čistý až na doplněný trailing newline).
|
||||
|
||||
**Vedlejší nález a fix — agent přepisoval `memory-clean.log`:** V prvním testu změnové cesty log po zápisu **zmenšil** (246 → 196 B) — starý řádek z 07-19 zmizel. Tohle je stejná ztráta, jaká už 07-15 spolkla zápisy o reálném mazání; prózové „append-only" nestačí, když má agent `write_file`, a moje nová formulace („log obsahuje jen smazané položky") ho ještě pozvala nevyhovující řádek „uklidit". Fix: `SKILL.md` teď předepisuje **deterministický append přes `exec`** (`printf '%s\n' '…' >> log/memory-clean.log`), zakazuje `write_file`/`edit_file` na log a explicitně zakazuje mazat/reformátovat existující řádky, i když neodpovídají aktuálnímu formátu. Po redeployi třetí běh: 196 → 350 B, starý řádek zůstal ✓. (Oba záznamy skončily na jednom řádku, protože předchozí zápis neměl trailing newline — soubor teď newline má, takže se to samo srovná.)
|
||||
|
||||
**Co zbývá:** Zkontrolovat noční běh 2026-07-26 v `log/compact_memory_auto_cron.log` (řádek `Response to cli:user:` má být krátký).
|
||||
|
||||
**Jak vrátit zpět:** `git checkout ef0d6cc~1 -- skills/compact-memory/` + `rsync -av skills/compact-memory/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/compact-memory/`. Testovací zásahy do `MEMORY.md` jsou už vrácené (agent smazal právě ty vložené odrážky); zálohy z běhů jsou v `backup/2026-07-25_06*`.
|
||||
|
||||
## 2026-07-26 — compact-memory: model navrhuje, skript validuje a aplikuje (konec prompt-compliance)
|
||||
|
||||
**Cíl:** Noční běh 2026-07-26 02:00 poslal na Telegram 1737 znaků narace („Now I have all four files. Let me audit MEMORY.md systematically…"), přestože včerejší oprava přesně tohle zakazovala. Uzavírá položku „zkontrolovat noční běh 07-26" z předchozího záznamu — a odpovídá na ni negativně.
|
||||
|
||||
**Diagnóza:** Skill na serveru identický s repem (`diff -ru` → IDENTICAL), takže žádný drift. V session `compact-memory-auto_20260726-020001.jsonl` je finální assistant zpráva 1737 znaků: celá úvaha jako text, na konci správný jednořádkový report. Skript posílal `result.content` beze změny → doručilo se všechno. Srovnání s včerejšími testy (56 a 165 znaků, 3 iterace, dávkové tool cally) vs. dnešek (8 iterací po jednom volání, soubory čtené přes `grep pattern="."`): stejný skill, stejný `GOAL`, stejný model `glm-5.1:cloud`, jiné chování. Závěr: formát výstupu nesmí viset na prompt-compliance, 3 čisté testy nedokazují nic.
|
||||
|
||||
**Co jsem zkusil (návrh schválen v plan mode, plán `~/.claude/plans/dobre-pojme-vylepsit-ten-buzzing-quasar.md`):** Obrácení zodpovědností — agent v nočním režimu **needituje nic** a vrací jen JSON change-set (`{"changes": [{op, category, original, new_text, reason}]}`), skript ho validuje, aplikuje, zálohuje, loguje a **sám skládá českou zprávu**. Klíčové volby: `original`/`new_text` jsou **pole řádků** (ne string s `\n` — tam modely kazí escapování); položky se identifikují **doslovným textem, ne čísly řádků**, a blok se musí v souboru vyskytovat právě jednou (halucinovaný citát spadne na validaci místo aby smazal špatný řádek); cílový soubor je **konstanta ve skriptu**, ne pole v JSON (uživatelova připomínka: pole `file` je zbytečná ceremonie, když je whitelist jednoprvkový); limity (`reason` ≤ 120 znaků, `new_text` ≤ 3 řádky/300 znaků, `original` ≤ 20 řádků, smazání < 50 % souboru) jsou **chyba, ne oříznutí**; nevalidní výstup se až **3× vrací modelu** s konkrétním seznamem chyb ve stejné (jednorázové) session; **SHA-256 guard** pozná, kdyby agent soubor upravil sám — agent má `write_file`/`exec` pořád k dispozici, takže zákaz v promptu je jen prosba. Noční běh navíc přepnut z `glm` na preset **`kimi27`** (`kimi-k2.7-code:cloud`) přes `Nanobot.from_config(model_preset=…)` — dopad omezen na tento job. Tvrdá selhání (timeout, výjimka) nově taky posílají jednořádkovou hlášku, dřív skončila tiše exit kódem 1.
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
- **Offline testy parseru** (30 kontrol, `scratchpad/test_parser.py`, stub modulu `nanobot` + `importlib`): validní delete/merge, prázdný change-set, bare JSON bez fence, JSON obalený narací, `reason` přes limit, citát co v souboru není, citát vyskytující se 11×, neznámé pole, špatná kategorie/`op`, merge co nezkracuje, překryv bloků, smazání 19 z 28 řádků, sběr chyb ze všech položek najednou — plus aplikace na kopii (záloha, append-only log, přestavěný soubor) a formát reportu. Vše zelené.
|
||||
- **Test 1 (nic k smazání):** jeden pokus, model vrátil přesně `{"changes": []}`, exit 0, hash `MEMORY.md` beze změny, log 350 B beze změny, žádná nová záloha.
|
||||
- **Test 2 (reverzibilní změna):** vložena ephemeral odrážka → model ji jako jedinou navrhl smazat, skript aplikoval, `diff` proti pretest kopii čistý, log 350 → 550 B (starý řádek zachován), nová záloha `backup/2026-07-26_0648_memory.backup.md`.
|
||||
- **Test 3a (chybová cesta):** kopie skriptu s `GOAL`, který vynucuje prózu → 3 pokusy, model postupně konvergoval (próza → JSON bez `changes` → JSON se špatným `op`), exit 1, na Telegram 145 znaků `Memory compact: CHYBA — po 3 pokusech: …`, `MEMORY.md` nedotčen.
|
||||
- **Test 3b (zotavení po odmítnutí):** kopie s uměle odmítnutým prvním pokusem → follow-up přijat, druhý pokus prošel, exit 0.
|
||||
- **`kimi27` uspěl na první dobrou** ve všech reálných bězích a i pod chybovým tlakem reagoval na konkrétní validační hlášky.
|
||||
|
||||
**Co zbývá:** Zkontrolovat noční běh 2026-07-27 (`log/compact_memory_auto_cron.log`) — odpověď modelu tam teď bude JSON, ale doručená zpráva z ní nevzniká. Zvážit zápis do `knowledge.md` (jak SDK přepíná model per běh, seznam presetů) a návrh do `decisions.md` (viz níže, čeká na formulaci autora). Interaktivní režim skillu zůstal beze změny a nebyl retestován.
|
||||
|
||||
**Pozor na `__pycache__`:** Lokální test skriptu přes `importlib` vyrobil `skills/compact-memory/scripts/__pycache__/`, který se `rsync -av` dostal na server (smazáno na obou stranách). Deploy skillů se skripty dělat s `--exclude='__pycache__'`.
|
||||
|
||||
**Jak vrátit zpět:** `git checkout 9c82894~1 -- skills/compact-memory/` + `rsync -av --exclude='__pycache__' skills/compact-memory/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/compact-memory/`. Testovací zásahy do `MEMORY.md` jsou vrácené (ověřeno `diff` proti kopii pořízené před testy, hash `249015fd…` shodný před i po). Testovací záznam v `log/memory-clean.log` a záloha `backup/2026-07-26_0648_*` zůstávají jako stopa po testu — log je append-only, nemažou se.
|
||||
|
||||
## 2026-07-27 — compact-memory: anglické hlášky + testy, a ruff/ty lint hook pro celý repo
|
||||
|
||||
**Cíl:** Tři věci na jedno zadání. (1) `compact_memory_auto.py` měl anglické docstringy, prompty i validátorové hlášky, ale sedm uživatelských hlášek zůstalo česky — doručený report byl jazykově rozpolcený. (2) Skript má bohatou čistou logiku (parser, validátor, přestavba souboru, report), ale jako jediný netriviální skill neměl `tests/`; offline testy parseru z 07-26 žily jen ve scratchpadu a zmizely. (3) V repu nebyla žádná Python lint konfigurace ani hook, přestože `skills/python/SKILL.md` definuje hotovo jako `ruff format` + `ruff check --fix`.
|
||||
|
||||
**Co jsem zkusil:** Plán `~/.claude/plans/vsechny-hlasky-do-anglictiny-curious-lantern.md`, schválený v plan mode.
|
||||
|
||||
- **Jazyk:** přeloženo všech 7 českých hlášek (`nebylo nic k smazání` → `nothing to remove`, `CHYBA —` → `ERROR —`, atd.). Součástí byla i změna `SKILL.md`: pravidlo `reason` — max 120 characters, **in Czech** → **in English** plus dva příklady v JSON schématu. Bez toho by report zůstal míchaný, protože `reason` od modelu se vkládá doslova do Telegram zprávy i do `log/memory-clean.log`.
|
||||
- **Testy:** nový `skills/compact-memory/tests/test_compact_memory_auto.py` — 51 testů, vzor podle `skills/note/tests/test_note_compile.py` (`sys.path.insert` na `../scripts`, `tmp_path` fixtura). Pokrývá `_extract_json`, validaci všech polí, agregaci chyb, hledání/unikátnost bloku, překryv, 50% limit, `apply_change_set` (obsah, záloha, append-only log, počet řádků), `format_report`, `_log_line`, `_shorten`, `_quote`, `_telegram_config`, `_workspace`. Aby to šlo importovat bez `nanobot-ai`, přesunut `from nanobot import Nanobot` z module-level do `_run()` + `TYPE_CHECKING` import pro anotaci — stejný vzor, jaký už používá `note_compile.py`.
|
||||
- **Lint:** nový `ruff.toml` (line-length 120, target py311, `select = ["E","F","I","UP","B","BLE","SIM","C4","RUF"]`), nový `ty.toml` (extra-paths na všechny `skills/*/scripts`, `unresolved-import = "ignore"`), nový hook `.claude/hooks/python-check.sh` registrovaný jako třetí položka v existující `PostToolUse` skupině `Edit|Write|MultiEdit`.
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
- **Hook je fail-fast a ověřený všemi cestami:** non-`.py` → exit 0; soubor pod `tmp/` → exit 0; neopravitelný ruff nález (`F821`) → exit 2 s `ruff findings`; čistý lint ale typová chyba → exit 2 s `ty findings`; čistý soubor → exit 0. Auto-fix ověřen (nepoužitý import ruff sám smazal). Latence: ruff ~10 ms, ty ~95 ms.
|
||||
- **`unset VIRTUAL_ENV` v hooku je nutnost, ne opatrnost.** V tomhle prostředí je proměnná zděděná a ukazuje na neexistující `/home/lachtan/.pyenv/versions/3.11.5`; `ty` na tom padá s `Failed to discover local Python environment` ještě před kontrolou.
|
||||
- **Explicitní `select` v `ruff.toml` je taky nutnost.** Ruff 0.16 má bez configu velmi širokou výchozí sadu (125 nálezů repo-wide včetně `DTZ*` a `EXE001`) a délku řádku 88, což odporuje `.claude/rules/python.md` (120). `BLE` je ve výběru schválně: skript má dvě promyšlená `# noqa: BLE001` a bez `BLE` by je `RUF100` označil za mrtvá a `--fix` je i s vysvětlujícím komentářem smazal.
|
||||
- **Ruff sám opravil, co jsem nehledal:** `asyncio.TimeoutError` → `TimeoutError` (`UP041`) a sloučení tří zalomených `raise ChangeSetError(...)` na jeden řádek (dřív zalomené kvůli 88 znakům).
|
||||
- **`ty` našel reálnou chybu:** `sorted(set(raw) - allowed)` — klíče jsou po `isinstance` zúžení typované `object`, což nesplňuje `SupportsRichComparison`. Opraveno na `sorted(str(key) for key in set(raw) - allowed)`.
|
||||
- **Server byl před nasazením identický s repo HEAD** (`rsync` do `tmp/srv-check-cm/` + `diff -r`) — žádný drift od Dream procesoru. Po nasazení `md5sum` shodné na obou stranách, vlastník `nanobot:nanobot`, `py_compile` na serveru OK, crontab beze změny.
|
||||
|
||||
**Zádrhel — PEP 723 přepíná `ty` do script-mode, kde projektový `ty.toml` neplatí na pravidla.** `unresolved-import = "ignore"` fungovalo na běžné moduly, ale ne na `compact_memory_auto.py`. Izolováno probe souborem: identický soubor **s** `# /// script` hlavičkou hlásí `unresolved-import`, **bez** ní projde. `--project .` nepomůže; `--config "rules.unresolved-import='ignore'"` ano, ale flag v hooku by znamenal, že ruční `ty check` dělá něco jiného než hook. Řešení: inline `# ty: ignore[unresolved-import]` u obou `nanobot` importů + komentář v `ty.toml`, že sekce `[rules]` na PEP 723 skripty nedosáhne.
|
||||
|
||||
**Zádrhel — `rsync` dvou souborů naráz je položil do stejného cílového adresáře.** `rsync SKILL.md scripts/compact_memory_auto.py server:.../compact-memory/` udělal na serveru `compact-memory/compact_memory_auto.py` vedle `compact-memory/scripts/compact_memory_auto.py`. Stray kopie smazána, skript nasazen zvlášť do `scripts/`. Příště buď celý adresář, nebo jeden soubor na jeden `rsync`.
|
||||
|
||||
**Co zbývá:** Ostrý noční běh 2026-07-28 02:00 — první, který poběží s anglickými hláškami a anglickým `reason`; zkontrolovat `log/compact_memory_auto_cron.log` a nové řádky v `log/memory-clean.log` (historické české řádky zůstávají, log je append-only). Manuální ostrý běh jsem nedělal — mutuje reálný `MEMORY.md` a posílá Telegram. Repo-wide zbývá 92 ruff a 16 ty nálezů v ostatních skriptech; hook je nahlásí, až se těch souborů někdo dotkne (nejvíc `skills/llm-wiki/scripts/wiki_lint.py` = 9). Ostatní skripty s českými hláškami (`tasks-daemon.py`, `wiki_compile.py`, `note_compile.py`) zůstaly podle rozhodnutí česky. `.claude/rules/bash.md` řádek 45 tvrdí, že bash formátování hlídá `check-bash.sh` PostToolUse hook — ten v repu neexistuje ani není registrovaný; dokument je rozsynchronizovaný.
|
||||
|
||||
**Jak vrátit zpět:** `git checkout <tento commit>~1 -- skills/compact-memory/ .claude/settings.json` + `rm ruff.toml ty.toml .claude/hooks/python-check.sh` + `rsync -av --exclude='__pycache__' --exclude='tests' skills/compact-memory/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/compact-memory/`. Na serveru se nic nemazalo ani nemutovalo mimo dva přepsané soubory (`SKILL.md`, `scripts/compact_memory_auto.py`) — `MEMORY.md`, zálohy ani logy zásah nepotkal.
|
||||
|
||||
## 2026-07-27 — Kontrola presetu `kimi3` (Kimi K3 na Ollama Cloud) + oprava context window
|
||||
|
||||
**Cíl:** Ověřit, že nově vydaný Kimi K3 (`kimi-k3:cloud` na Ollamě) je v `~/.nanobot/config.json` nastavený správně a má správné parametry.
|
||||
|
||||
**Co jsem zkusil:**
|
||||
|
||||
- `ssh nanobot@nanobot.hell` → dump `config.json`. Pozor: top-level klíč je `model_presets` (snake_case), ne `modelPresets`, jak tvrdila knowledge — první pokus o výpis vrátil `null`.
|
||||
- `curl http://nvidia.hell:11434/api/tags` (model v katalogu je) a `api/show` pro `kimi-k3:cloud` i pro srovnání `kimi-k2.6`, `kimi-k2.7-code`, `glm-5.1`, `glm-5.2`.
|
||||
- Funkční test: `curl .../v1/chat/completions` s `kimi-k3:cloud`, pak totéž s `glm-5.2:cloud` a `kimi-k2.7-code:cloud` jako kontrolní skupina.
|
||||
- `journalctl --user -u nanobot.service` — hledání stop po přepnutí na preset.
|
||||
- Online ověření na žádost uživatele: [ollama.com/pricing](https://ollama.com/pricing), [ollama.com/library/kimi-k3](https://ollama.com/library/kimi-k3), oznámení Ollamy na X.
|
||||
- `curl https://openrouter.ai/api/v1/models` — hledání alternativní cesty ke stejnému modelu.
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
- **Konfigurace presetu je věcně správná.** `kimi3` = `{model: kimi-k3:cloud, provider: ollama, maxTokens: 16384, temperature: 0.1, reasoningEffort: null}`. Název modelu přesně sedí s `api/tags`, `apiBase` má povinný `/v1` suffix, model má capabilities `tools`+`thinking`+`vision`. Journal potvrdil, že runtime přepnutí funguje: `Runtime model switched for next turn: glm-5.1:cloud -> kimi-k3:cloud`. `reasoningEffort: null` není opomenutí — `schema.py:141` říká, že `None` zachovává default providera.
|
||||
- **Blokuje to Ollama, ne config.** Přímé volání endpointu vrací HTTP 402 `this model uses extra usage only (not included plan usage) and your extra usage balance is empty`. Kontrolní skupina (`glm-5.2:cloud`, `kimi-k2.7-code:cloud`) přitom odpovídá normálně → není to stav účtu ani API klíč, je to per-model politika. Online potvrzeno: K3 vyžaduje Pro/Max **a** nenulový dokoupený extra usage balance, kapacita se teprve rozšiřuje. Nanobotí hláška *„API key is out of quota or the account is in arrears"* je zavádějící překlad tohoto 402.
|
||||
- **Oprava parametru:** `contextWindowTokens` bylo `967000` proti reálnému `1048576` (z `api/show`, klíč `kimi-k3.context_length`) — ~81k tokenů ležel ladem. Ostatní presety drží ~97 % reálného okna (`glm-5.1`: 196608/202752, `glm-5.2`: 976000/1000000, kimi K2.x: 262144 = přesně). Srovnáno na **1 020 000**. Záloha `~/.nanobot/backup/config.json.bak-20260727-202245`, `diff` proti ní ukazuje jediný změněný řádek, vlastník `nanobot:nanobot` a práva `600` beze změny. Restart nebyl potřeba — presety se načítají každý tah (`_refresh_provider_snapshot`).
|
||||
- **Vedlejší nález:** knowledge tvrdila konvenci pojmenování presetů `<model>-<provider>` (`kimi-k2.6-openrouter`), reálně jsou na serveru krátké aliasy (`kimi`, `kimi27`, `kimi3`, `glm`, `glm52`, `sonnet`, `haiku`, `gemini-flash`). Opraveno v `knowledge.md` spolu s názvem klíče `model_presets`.
|
||||
|
||||
**Co zbývá:** Model je do dobití extra usage na ollama.com nepoužitelný. Alternativa bez dobíjení, pokud ji uživatel bude chtít: preset přes OpenRouter (`moonshotai/kimi-k3`, ctx 1 048 576, $3/M in + $15/M out) — provider `openrouter` je v configu už nakonfigurovaný. Nezakládal jsem ho, nebylo zadáno.
|
||||
|
||||
**Jak vrátit zpět:** `ssh nanobot@nanobot.hell 'cp ~/.nanobot/backup/config.json.bak-20260727-202245 ~/.nanobot/config.json'` — mimo tuhle jednu hodnotu jsem na serveru nic neměnil.
|
||||
|
||||
Reference in New Issue
Block a user