229 KiB
History
2026-07-02 — bookmark: zestručnění frontmatter description (routing-focused), nasazeno
Cíl: Popis skillu byl moc dlouhý, prozaický a obecný („Manage a personal reading list…"). Přepsat na krátký, foldovaný (>) a zaměřený na kdy aktivovat, ne na výčet funkcí.
Co jsem zkusil / co fungovalo: Několik iterací s uživatelem (on sám mezitím editoval v IDE). Finální verze: Use when the user wants to save a URL (with or without additional content) to read later or search in. Triggers on "bookmark", "save URL". Cestou opraveny drobnosti (an URL→a URL, uzavření vsuvky, rozbité souvětí) a odstraněn zbloudilý ks mezi frontmatterem a # Bookmark. Detailní výčet příkazů zůstává v těle skillu, kam patří.
Nasazení: Server vs lokál se lišil jen v description (tělo identické → žádný Dream edit k dotažení). Nahrán rsync jen SKILL.md (ne celý adresář — bookmark.py a html_to_markdown.py jsou lokálně rozdělané a nasazení nebylo požadováno). Owner nanobot:nanobot ✓. Skill se čte per-tah bez restartu.
Co zbývá: Rozdělané změny skriptů (bookmark.py, html_to_markdown.py, testy) — nenasazeno, čeká na dokončení.
Jak vrátit zpět: git checkout HEAD -- skills/bookmark/SKILL.md (po commitu příslušný git revert), pak rsync na server.
2026-06-17 — llm-wiki: oprava reference souborů z code-review (10 nálezů)
Cíl: Vykonat schválený plán oprav reference souborů skillu (review byl zaměřený na references/). Nálezy: bare cesty + kroky workflow předpokládající interaktivního uživatele navzdory headless compile (cron drain).
Co jsem zkusil / klíčový vhled: SKILL.md ř. 22 má globální override „kdekoli docs říkají wiki/, čti cml/wiki/" — takže bare-wiki/ cesty v referencích nejsou rozbité, jen přesměrované. Většina path-nálezů z review tím padá jako ne-bug. Reálné bugy jsou ty, které override nezachrání: kroky „discuss/triage with the user" (v drainu není uživatel → uvázne nebo tiše přeskočí, graf zastará) a „regenerate graph first" v query tahu (compile práce v interaktivním tahu). Server vs repo diff před i po prázdný (žádný Dream edit). Vykonání delegováno na sonnet agenta dle konvence repa; sweep cest udělán boundary-aware perlem ((?<![-/\w.])wiki/), aby nerozbil llm-wiki/ ani wiki_*.py.
Co fungovalo a proč: Skupina A (4 fázové bugy): ingest Step 3 → interactive-only + drain skip; ingest Step 8b + graph Step 1 → Interactive/Drain větve (lint čistý→extract, chyby→log+skip, neblokovat); query Step 2b + graph ř. 106 → graf v query neregenerovat; divergentní skip podmínky sjednoceny na graph: metadata v obou souborech. Skupina B: na žádost uživatele plný sweep wiki/→cml/wiki/, raw/→cml/raw/ (literál bezpečnější než spoléhat na override; reference byly stejně už půl-převedené) — skills/llm-wiki/scripts/ netknuté, ověřeno grepem. Skupina C: smazán references/agent-memory-integration.md (SKILL.md ho na nanobotu stejně přeskakuje — wiki se nedrátuje do agent-memory) + úklid 2 odkazů v SKILL.md; odstraněna ne-akční sekce „Why this architecture" z architecture.md. Nasazeno rsync (smazaný soubor řešen explicitním ssh rm, ne --delete), owner nanobot:nanobot ✓, server-side spot-check čistý.
Co zbývá: Volitelný smoke test drainu na serveru (1 testovací zdroj — že neuvázne na „discuss with user"). 4 follow-up zlepšení (akční lint data, frontmatter šablona, narrow --fix, slug kolize) zůstávají v todo.md.
Commit: c74e7de. Jak vrátit zpět: git revert c74e7de (nebo git checkout c74e7de~1 -- skills/llm-wiki/), pak rsync na server + obnovit smazaný soubor z gitu.
2026-06-17 — llm-wiki: tvrdá report-only brána u lintu (lint tah se zacyklil 24 min)
Cíl: Uživatel hlásil, že lint nad wiki je „šíleně pomalý", zasekl se hned napoprvé, točil se 24 min a sežral hafo tokenů. Najít příčinu (bez opravy), pak opravit.
Co jsem zkusil / příčina: Z transkriptu: lint spustil wiki_graph_lint.py (nahlásil ~20–30 chyb), ale místo report→STOP spadl do plného opravného režimu — 7 nových stránek, 30+ editací, debug skripty do tmp/, regenerace grafu, re-lint ve smyčce; wiki_graph_lint.py přečetl ~15× po sobě (vlastní slova: „četl jsem ty samý soubory dokola"). Příčiny: (A) „Lint is report-only" byla jediná měkká odrážka v SKILL.md, kterou referenční lint-workflow.md přímo popírala — má 6krokový postup celý o mutaci wiki (Step 2 fix, Step 5 index, Step 6 log), strukturálně čte jako „oprav vše a zaloguj". Stejná chyba jako capture/compile brána (history 16.6. 22:45). (B) Žádný strop na opravnou/debug smyčku; graph lint je provázaný (oprava jedné hrany vyrobí orphan) → ruční edity ho těžko uspokojí → smyčka edit↔lint. (C) Skript sám rychlý (0,15–0,26 s na téhle wiki) — 24 min byla 100 % agentní smyčka, ne CLI.
Co fungovalo a proč: Server=zdroj pravdy: rsync server→tmp/, diff SKILL.md+references/lint-workflow.md = IDENTICAL (žádný Dream edit). Fix (recept z capture brány): (1) tvrdá report-only brána v SKILL.md ř.26 — lint tah = spusť 2 skripty, předej report, STOP + výčet zakázaných akcí (žádné stránky, debug skripty, regenerace grafu, re-lint smyčka); (2) „lint workflow (summary)" přerámován na report-first + STOP-redirect + strop na opravnou smyčku (bounded batches, necti skript opakovaně, re-lint max 1× na ověření, pak ptej se); (3) lint-workflow.md dostal report-only banner nahoře + mutační kroky (2/5/6) označeny „fix turn — only after approval". Nasazeno rsync (přenesly se jen 2 soubory), owner nanobot:nanobot ✓. Skill se čte per-tah bez restartu.
Co zbývá: Behaviorální ověření dělá uživatel — příští lint tah musí jen nahlásit a zastavit. Vyhodnotit 7 follow-up návrhů nanobota (--fix mód, akční lint data, frontmatter šablona, slug kolize při add, timeout per krok).
Jak vrátit zpět: git checkout HEAD~1 -- skills/llm-wiki/SKILL.md skills/llm-wiki/references/lint-workflow.md, pak rsync na server.
2026-06-17 — llm-wiki: code review + oprava 4 chyb v capture bráně + zeštíhlení
Cíl: Code review skillu llm-wiki (correctness + verbosity). Uživatel požádal opravit vše.
Co jsem zkusil / co fungovalo:
- Review odhalil 4 correctness chyby a 6 verbosity problémů.
- Correctness opravy: (1) ⚠️ mega-odstavec zkrácen na 1-větový pointer — odstranil zdroj rozporů s řádky 38/39; (2) URL při capture: "fetch nebo zapiš" → vždy zapiš as-is; (3) idempotence "pages already exist" zpřesněno na konkrétní soubor
cml/wiki/sources/<slug>.md; (4) escape hatch triggers nyní jen na jednom místě. - Verbosity: odstraněny sekce "The pattern in one paragraph" a "When to use this skill" (duplikát frontmatter); directory tree opraven na reálné
cml/wiki//cml/raw/cesty; "Failure modes" ze 4 odstavců na 2 odrážky; "Bundled scripts" katalog odstraněn (wiki_stats přesunut do scalability). - Výsledek: 198 → 169 řádků (−17 %). Nasazeno na server.
Commit: 239dc4f
2026-06-17 05:15 — llm-wiki: zeštíhlit description na čistý trigger (+ folded ≤100 zn.)
Cíl: description ve frontmatteru SKILL.md byl ukecaný (~1060 zn.) — míchal kdy spustit (routing signál) s jak funguje (výklad). Dvě kola: (1) zalomit na ≤100 zn., (2) na žádost uživatele zeštíhlit obsah na čistý trigger.
Mechanika (vysvětleno uživateli): Do baseline kontextu jde jen jednořádkový description (routing signál — nanobot/agent/skills.py, viz knowledge.md „Skill description — progressive loading"). Tělo SKILL.md se čte on-demand při použití skillu; references/*.md nezná ani skill loader — jsou to obyčejné soubory čtené po jednom přes read_file, jen když na ně tělo navede. Rozsah referencí je proto kontextově fakticky zadarmo → osekávat se mají kvůli srozumitelnosti, ne tokenům.
Co jsem udělal:
- Block scalar
|(literal — zachoval by vložené\n) →>(folded — řádky se složí na mezery), zalomeno na ≤100 zn. Folded scalar = v seznamu skillů se renderuje jako jeden řádek; v 1. kole ověřenoyaml.safe_load, že načtená hodnota je bajt po bajtu identická. - Zeštíhlení obsahu (~1060 → ~480 zn.): vyhozeno (a) atribuce + datum („from Andrej Karpathy's April 2026 gist", „OmegaWiki") — origin, nulová routing hodnota, Karpathy už je v těle (SKILL.md:9); (b) how-it-works věta o škálování — už v sekci The scalability discipline; (c) dlouhý literální výčet trigger frází — redundantní (
knowledge.md2026-05-31: model pozná záměr napříč formulacemi). Ponecháno: co dělá + kdy (ingest/ask/lint/init), „i když uživatel neřekne wiki", a rozlišení vůči agent paměti (note/keep/MEMORY.md) — to má reálnou routing hodnotu. Tělo SKILL.md beze změny.
Verifikace + deploy: Server=zdroj pravdy: před každým kolem rsync server→tmp/, diff → IDENTICAL (Dream nesahal). Po deployi rsync zpět + diff → IDENTICAL, owner nanobot:nanobot OK. Skilly se čtou bez restartu.
Jak vrátit zpět: git revert <commit> + rsync předchozí verze SKILL.md na server. Zalomení je funkčně no-op; zeštíhlení mění obsah triggeru (užší, ale sémanticky pokrývá totéž).
2026-06-14 14:30 — note: tag u multi-line poznámky na header + spolknout osiřelý oddělovač
Cíl: V /note list (CLI/nanobot agent) u poznámky, jejíž URL _urls_on_own_lines rozhází na vlastní odrážky, padal tag za poslední URL odrážku (vizuálně nesmysl) a na konci headeru visel osiřelý oddělovač — + na první odrážce koncová čárka — pozůstatky inline formy (… od vína — Universal: url, Amber: url) po přesunu URL na odrážky.
Co jsem udělal (skills/note/scripts/note.py):
_LABELED_URL_RE: prefix\s*→ třída oddělovačů[\s,;—–-]*, takže připojený konektor před URL/labelem padne do nezachyceného prefixu a zahodí se. Mizí osiřelý—i,mezi URL. DB icmd_shownetknuté (drží původní obsah verbatim).cmd_list: tag vkládám za první řádek (partition("\n")→head+ tag +sep+rest) místo na konec celého bloku. Jednořádkové poznámky beze změny (partitionvrátí(text,"","")).
Verifikace + deploy: Lokální test helperů na konkrétní poznámce (DT GLASS) + jednořádkové → OK. Server=zdroj pravdy: rsync server→tmp/note.server.py, diff proti lokálu → IDENTICAL (Dream nesahal) → deploy bezpečný. rsync -av skills/note/ na server, owner nanobot:nanobot OK. Reálný uv run … list na serveru (přes ~/.local/bin/uv, non-login PATH gotcha) → položka 7 má tag na headeru, čisté odrážky bez —/čárky, ostatní beze změny.
Souvislost: Čistě textový výstup skriptu. Nevyřešený WebUI card-renderer problém (plans/note-wrong-urls.md) tím není dotčen.
Jak vrátit zpět: git revert <commit> skillu (zatím necommitnuto) + rsync předchozí verze na server. DB beze změny.
2026-06-13 07:45 — note: posílit show k doslovnému plnému výpisu
Cíl: Agent při /note show zkracoval výstup a vypouštěl odkaz/URL z obsahu — defaultní terseness ze SOUL.md přebíjela slabou instrukci echo the structured block unchanged. Skript vypisuje plný blok správně, problém byl jen v prezentaci.
Co jsem udělal: V SKILL.md ## Show protocol přepsal bod Exit 0 na emfatickou direktivu: output the script's stdout verbatim — print every line exactly as emitted; do not summarize, shorten, rewrap, or drop any part of content including URLs/links; brevity directives do not apply here. Žádná změna kódu skriptu.
Verifikace + deploy: diff serverové SKILL.md proti lokálu → jediný rozdíl moje nová úprava (server jinak nezměněn) → deploy bezpečný. rsync SKILL.md na server, owner nanobot:nanobot OK. Skilly se čtou bez restartu. Reálné ověření chování agenta na URL poznámce zbývá uživateli.
Jak vrátit zpět: git revert <commit> + rsync předchozí verze SKILL.md na server.
2026-06-13 07:40 — note: nový subcommand show (jedna poznámka v plném znění)
Cíl: /note uměl add/list/delete, ale ne vytáhnout jednu poznámku se vším uloženým. Přidat show <display-id>, který vypíše všechna pole (display ID, DB id, datum, tagy, obsah) ve formě vhodné pro LLM.
Co jsem zkusil / udělal:
- Backend (skills/note/scripts/note.py):
cmd_show— mapování display ID → DB id přes existující_active_ids(stejná logika jakocmd_delete), guard na rozsah s exit 1 + hláškouNo active note with display id=N.. SELECT přidávácreated_at(delete ho nečte). Tiskne strukturovaný blokNote [#N] (id=…)/created:(ISO 8601 UTC beze změny) /tags:(#tagmezerou, jinak(none)) /content:. LogSHOWkonzistentně sLIST. Subparsershow+ dispatch větev. - SKILL.md (skills/note/SKILL.md): sekce
## Show protocol(triggery, read-only, display IDs jako u list/delete) + edge case/note showbez ID. - Verifikace lokálně: add (s tagy / bez) → list →
showu obou (tagovaná itags: (none)) →show 999exit 1 → delete úklid. Vše OK. - Server = zdroj pravdy:
rsyncserver→tmp/note-server/,diffSKILL.md i note.py → jediný rozdíl jsou moje novéshowpřírůstky, žádná autonomní divergence (Dream nesahal) → deploy bezpečný. - Deploy:
rsync -av skills/note/na server, ownernanobot:nanobotOK. Smokeshow 999na serveru přes plnou cestu/home/nanobot/.local/bin/uv(interaktivníuvv non-login SSH PATH chybí — známý gotcha) → exit 1, OK.
Co fungovalo a proč: Skilly se čtou bez restartu. show je čistě čtecí — žádný dopad na DB ani delete logiku. Reuse _active_ids znamená, že display IDs jsou konzistentní napříč list/show/delete.
Co zbývá: Commit (tracking + skill zvlášť dle konvence).
Jak vrátit zpět: git revert <commit> skillu + rsync předchozí verze na server. DB beze změny.
2026-06-10 10:47 — remind: nový subcommand upcoming (co a kdy odejde)
Cíl: /remind neuměl dopředu říct, co a kdy odejde. Výpočet kdy reminder vystřelí už existoval (remind_send.py _due_*), šlo o jeho vystavení jako dotazu — bez nafouknutí remind_cli.py (491 řádků).
Co jsem udělal:
- Nový sdílený modul
scripts/forecast.py—window_for(),fires_in_window(),format_upcoming(). Veškerá výpočetní + formátovací logika sem; CLI dostalo jen tenký handlercmd_upcoming. - Znovupoužity hotové primitivy:
compute_fire_times(random),croniter.get_next(cron), ISO string compare (at). Forecast je čistá projekce rozvrhu — bez dedupu protireminder_firesa bez tolerance (na rozdíl oddelivered, který ukazuje skutečnost). - DRY: sestavení cfg z
schedule_randomřádku vytaženo dorandom_times.random_cfg_from_row(), použito veforecastiremind_send(daemon jinak netknutý). - Rozhraní:
upcoming(= zbytek dneška),--date YYYY-MM-DD(celý den),--days N(N kalendářních dní vč. dneška);--date+--daysse vylučují. - SKILL.md: řádek do NL→command tabulky + věta do behavioral contractu.
- Testy
tests/test_forecast.py(16 testů: window_for, at/cron/random, filtry, řazení, formát).
Co fungovalo a proč: Lokálně 46 testů green (uv run --with pytest --with croniter pytest). Smoke test CLI proti temp DB i mutual-exclusion chyba OK. Nasazeno na server rsync -av (vlastník nanobot:nanobot), upcoming --days 2 proti ostré DB vypsalo korektní rozvrh.
Jak vrátit zpět: git revert příslušného kódového commitu; na serveru smazat scripts/forecast.py a rsync předchozí verze random_times.py/remind_cli.py/remind_send.py/SKILL.md.
Pozn.: uv na serveru není v non-interaktivním SSH PATH — je v ~/.local/bin/uv (cron používá shebang #!/usr/bin/env -S uv run --script).
2026-06-07 19:57 — imageGeneration: provider z openrouter na ollama (žádný paid path)
Cíl: tools.imageGeneration v serverovém configu odkazoval na openai/gpt-5.4-image-2 přes provider openrouter (placený). Uživatel nechce, aby cokoli kolem obrázků šlo přes openrouter — jen ollama provider / cloud modely.
Zjištění:
openai/...byl jen default vypnutého toolu (enabled: false) — je to OpenRouter naming conventionvendor/model, ne odkaz na (neexistující) OpenAI provider. Providery nakonfigurované jen tři:openrouter,ollama,gemini.- Nanobot podporuje image-gen přes ollama (
OllamaImageGenerationClient→POST /api/generateswidth/height/steps). - ALE Ollama Cloud nemá žádný text→image model — ověřeno z
ollama.com/search?c=cloudi zcurl nvidia.hell:11434/api/tags: samé LLM, vision-LLM (obrázky na vstupu) a embeddingy, žádný flux/SD/diffusion. Takže image-gen na ollama reálně nepoběží, není na co model namířit.
Co jsem udělal:
- Záloha:
cp -a ~/.nanobot/config.json ~/.nanobot/config.json.bak-imggen. - In-place Python edit
~/.nanobot/config.json→tools.imageGeneration:provideropenrouter→ollama,modelopenai/gpt-5.4-image-2→"",enabledponechánofalse. Config re-parsnut OK.
Co fungovalo a proč: Splňuje záměr — žádná cesta image-gen přes placený openrouter. Tool je enabled: false, takže neutrácí; i kdyby se zapnul, generace na ollama selže (žádný image model), nezačne nic platit.
Co zbývá / pozn.: Funkční generování obrázků by vyžadovalo placený provider (gemini/openrouter) nebo lokální diffusion mimo Ollamu. Na ollama to nejde.
Jak vrátit zpět: cp -a ~/.nanobot/config.json.bak-imggen ~/.nanobot/config.json.
2026-06-07 19:07 — Přidán model preset gpt (gpt-oss:120b-cloud, ollama)
Cíl: Přidat do serverové konfigurace nový model preset gpt = gpt-oss:120b-cloud přes Ollama.
Co jsem zkusil / udělal:
- Ověřil dostupnost:
curl http://nvidia.hell:11434/api/tags→gpt-oss:120b-cloudv seznamu (přesná shoda). - Záloha:
cp ~/.nanobot/config.json ~/.nanobot/backup/config.json.bak-20260607-190745. - In-place Python edit
~/.nanobot/config.json: domodel_presets(snake_case na disku) přidán klíčgptse strukturou jako ostatní ollama presety —provider: ollama,model: gpt-oss:120b-cloud,maxTokens: 16384,contextWindowTokens: 131072(gpt-oss-120b = 128k kontext),temperature: 0.1,reasoningEffort: null,label: null. - Restart neproveden —
model_presetsse hot-reloadují (_refresh_provider_snapshot()před každým tahem), preset je dostupný okamžitě přes/model gpt.
Co fungovalo a proč: In-place Python edit zachoval ostatní klíče včetně secrets. Hot-reload presetů ověřen v knowledge.md → žádný výpadek služby.
Jak vrátit zpět: cp ~/.nanobot/backup/config.json.bak-20260607-190745 ~/.nanobot/config.json (nebo Python del c["model_presets"]["gpt"]).
2026-06-07 18:08 — Detach skill: ms timestamp + detach.log + popis ve výpisu
Cíl: Opravit tři problémy v /detach skillu: (1) riziko přepsání souboru úlohy při shodě slugu a sekundy, (2) neúplné logování (create a archive nelogoval vůbec), (3) výpis list ukazoval jen slug bez popisu co úloha dělá.
Co jsem zkusil / udělal:
create-task.py: timestamp přepnut z%Y-%m-%dT%H%M%S(sekundy) na%Y-%m-%d_%H_%M_%S_%f(mikrosekundy); filename2026-06-07_15_00_00_123456-slug.md. Dvě souběžná volání se stejným slugem teď nemohou kolidovat na úrovni souboru.tasks_common.py: FILENAME_RE rozšířen pro oba formáty (starý T-joined zpětně kompatibilní); přidánparse_timestamp()s fallback na oba formáty; sdílenálog()→~/.nanobot/workspace/log/detach.log;render_table()přijímálist[Path]místolist[str]a přidává sloupecwhat(první věta# Goalsekce);goal_summary()helper.tasks-daemon.py: importujelogztasks_commonmísto vlastní implementace; maže lokálníWORKSPACE,LOG,log().create-task.py: logujeCREATE <filename> slug=<slug>po úspěšnémos.replace().archive-tasks.py: logujeARCHIVE <filename>pro každý přesun.list-tasks.py:list_dir()vracílist[Path], předává cestu dorender_table().- Testy: 56/56 pass; přidány testy pro nový timestamp formát,
parse_timestamp,goal_summary,render_tables reálnými soubory. - Nasazení:
rsync -av skills/detach/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/detach/(7 souborů).
Co fungovalo a proč: Mikrosekundový timestamp je deterministické a jednoduché řešení — žádný slug-dedup ani zámky. Sdílený log() v tasks_common.py je správné místo pro sdílené utility.
Jak vrátit zpět: git revert 587cb42 a redeployovat. Starý daemon log byl v ~/.nanobot/workspace/log/tasks-daemon.log — po revertu bude daemon logovat tam; detach.log zůstane prázdný (nevadí, není kritický).
2026-06-07 — /note: display ID (1..N) místo rostoucího AUTOINCREMENT ID
Cíl: Zabránit tomu, aby museli uživatelé psát stále větší čísla (např. delete 847) při mazání poznámek.
Co jsem zkusil: Přidat display ID jako druhé ID pro manipulaci — 1..N mezi aktivními poznámkami, nejnovější = 1, přepočítá se po každém smazání.
Co fungovalo a proč:
_active_ids(conn)vrátí seznam interních ID v pořadícreated_at DESCcmd_listbuildujeid_to_displaydict ze stejného pořadí → globálně konzistentní i při--tagfiltru a--offsetstránkovánícmd_deleteresolvuje display ID na interní ID přes_active_ids, log zaznamenává obě IDcmd_addhardcode#1— nová poznámka je vždy nejnovější- Nasazeno na server přes rsync
Co zbývá: Nic.
2026-06-07 — /note skill: tagy, soft-delete, operation log, zkrácený SKILL.md
Cíl: Přidat tagy, soft-delete místo fyzického mazání, append-only operation log a zkrátit/přepsat SKILL.md (fully EN, bez 5–15 word limitu).
Co jsem udělal:
note.pypřepsán: nové schema (tags TEXT DEFAULT '[]',deleted_at TEXT);_connect()context manager s WAL + row_factory (dlebookmark.pyvzoru);_migrate()idempotentně přidá chybějící sloupce existující DB;_validate_tags()regex^[a-z][a-z0-9-]*$;_tags_display()formátuje[#cli #tools];_log()ms-přesný timestampYYYY-MM-DD HH:MM:SS.mmm;cmd_delete()je soft (UPDATE, ne DELETE);cmd_list()filtrujedeleted_at IS NULL, podporuje--tagOR filtr přesjson_each();cmd_add()přijímá--tags.SKILL.mdpřepsán do EN: odstraněn 5–15 word limit → "one concept per entry, split if too complex"; přidána sekce Tag protocol (inline tagy jako první token,arch,hw,linux); delete protokol odkazuje na soft-delete alog/note.logjako primární audit trail.- Migrace proběhla automaticky při prvním spuštění na serveru — 4 existující záznamy dostaly
tags='[]',deleted_at=NULL.
Ověření: Lokální smoke test (add s tagy, list, --tag OR filtr, delete, invalid tag, log) → vše OK. Na serveru ověřeno list (4 záznamy), add+delete test záznamu, log/note.log obsahuje všechny operace.
Co zbývá: /keep — zvážit analogický přechod na Python skript + logování (otevřený úkol v todo.md).
2026-06-07 — Konsolidace skillů mind + note → jeden note (SQLite backend)
Cíl: mind a note byly funkční dvojče (stejný účel, triggery, protokol), lišily se jen backendem — mind SQLite přes mind.py, note plochý notes.md editovaný LLM. Zachovat SQLite variantu (dělba rolí + determinismus + budoucí logování, viz todo.md a serverová MEMORY.md), zrušit markdown variantu, a výsledek pojmenovat note (uživatelské jméno skillu).
Co jsem zkusil:
- Porovnání + rozhodnutí:
mindzachovat (skill orchestruje,mind.pydeterministicky zapisuje/čte/maže; máid+created_at; sedí na zamrzlé rozhodnutí „perzistentní SQLite vždydb/*.sqlite").note(LLM sám edituje markdown) je opak — nedeterministický, neloggovatelný. - Migrace dat: 3 poznámky ze serverového
notes.mdpřetaženy domind.sqlitepřesmind.py add(jazyk zachován, CZ). Po migraci 4 záznamy (id 2–5), nic se neztratilo. - Ověření shody se serverem:
rsync+diff -robou skillů → IDENTICAL, žádná Dream úprava k dotažení. - Přejmenování (lokálně):
git rm skills/note/SKILL.md(markdown),git mv skills/mind skills/note,git mv .../mind.py .../note.py. Vnote.py:DB_PATH→db/note.sqlite, docstring + argparse description. SKILL.md přepsánmind→notenapříč (frontmatter, triggery, cesty,# Note). Smoke test add/list/delete lokálně OK. - Nasazení: na serveru
mv db/mind.sqlite db/note.sqlite,rm -rf skills/mind skills/note,rm notes.md,rsyncnovéhoskills/note/. Ověřeno: ownershipnanobot:nanobot,note.py listvrací všechny 4 poznámky znote.sqlite, veskills/zbyl jennote+remind.
Co fungovalo a proč: SQLite varianta přežila, protože jako jediná splňuje směr zapsaný v todo.md (dělba rolí skill+skript) i v serverové MEMORY.md (/note: SQLite, deterministický, krátký prompt + python skript). Migrace přes add zachovala terse styl i jazyk. Skilly se čtou per-tah bez restartu → note hned aktivní.
Jak vrátit zpět: Data jsou v db/note.sqlite (jen přejmenovaná z mind.sqlite, ne smazaná). Obnova skillů: git revert/git checkout lokálně + re-rsync; server notes.md byl smazán, ale jeho 3 řádky žijí v note.sqlite (id 3–5).
Co zbývá: Druhé kolo — note.py zatím neloguje operace (má created_at per řádek, ale delete nemaže do žádného audit logu → nejde dohledat, proč/kdy něco zmizelo). Doplnit append-only operation log (op + obsah + čas) do skriptu. Sleduje todo.md a MEMORY.md požadavek „logovat všechny provedené operace".
2026-06-03 — MiniLoop gemini-flash-lite a mistral-small-3.2 cache
Cíl: Otestovat google/gemini-3.1-flash-lite (OpenRouter) a ověřit mistral-small-3.2.
Co jsem zkusil: dotnet run --no-build -- test gemini-flash-lite a dotnet run --no-build -- test mistral-small-3.2.
Co fungovalo a proč: gemini-flash-lite — 17/17 ok, median 683 ms, avg 719 ms, out 559 tok. Nejlepší výsledek ze všech dosud měřených OpenRouter modelů; poráží haiku-4.5 (1064 ms), gemma-3-27b (1413 ms) i glm-5.1-ollama (1690 ms). Žádný reasoning, přímý parse.
Problém — mistral-small-3.2 byl dřív naměřen s cache: Původní výsledek 934 ms median (history 2026-06-03 „MiniLoop levné OSS") zřejmě těžil z cache providera. Opakovaný cold test ukázal 3 400–7 400 ms na prvních 4 příkladech — přibližně 3–8× horší. Skutečná cold performance je cca 4–5 s median. Označeno v knowledge.md.
Co zbývá: ověřit gemini-flash-lite v plném produkčním nasazení (nanobot remind skill), zhodnotit cenu.
2026-06-03 — MiniLoop phi4:latest zamítnut
Cíl: Otestovat lokální phi4:latest (14.7B) jako kandidáta pro MiniLoop.
Výsledek: Zamítnut — 15/17, median 1744 ms / avg 1676 ms. Dvě skutečné chyby: (1) příští pondělí → 2026-06-05 (čtvrtek) místo 2026-06-08 — horší než codestral, který vrátil neděli; (2) dopoledne → window 09:00-12:00 místo 08:00-12:00. Navíc pomalejší než gemma4:e4b (1744 vs 1136 ms). „Punches above weight" reputace se pro tuto úlohu nepotvrdila.
2026-06-03 — MiniLoop codestral:22b zamítnut
Cíl: Otestovat lokální codestral:22b jako kandidáta pro MiniLoop.
Výsledek: Zamítnut — 16/17, median 2483 ms / avg 2621 ms, skutečná chyba data: příští pondělí → 2026-06-07 (neděle) místo 2026-06-08 (pondělí). Stejná třída chyby jako ministral-3:8b (weekday aritmetika). Zároveň 2× pomalejší než gemma4:e4b (1136 ms). Coder specializace nepomohla u česky popsaných relativních dat.
2026-06-03 — MiniLoop gemma4:e4b local
Cíl: Otestovat lokální gemma4:e4b (8B, nvidia.hell) v MiniLoop — jestli zvládne 17/17 a jaké jsou časy.
Co jsem zkusil: dotnet run -- test gemma4-local — jediný model, bez gate (lokální Ollama, žádná kvóta).
Co fungovalo a proč: 17/17, median 1136 ms / avg 1719 ms, out=1530 tok. Žádné chyby data ani cronu. Odlehlé hodnoty (3–4 s) u složitějších vstupů (random times, multiple windows). Výsledek překvapivě silný pro lokální 8B model — lepší median než glm-5.1 cloud (1690 ms) a blízko gemma-3-27b-it OpenRouter (1413 ms). Bez reasoning skluzu (porovnej nemotron-nano: out=7805).
Co zbývá: porovnat s dalšími lokálními modely (codestral:22b, phi4) pokud bude potřeba offline fallback.
2026-06-02 — Fix: dynamická detekce verze nanobotu v check_nanobot_version.py
Cíl: Opravit hardcoded CURRENT_VERSION = "0.2.0" v ~/.nanobot/workspace/scripts/check_nanobot_version.py — po upgradu na 0.2.1 by cron job navždy hlásil 0.2.1 jako "novou verzi".
Co jsem zkusil: Tři varianty detekce aktuální verze: nanobot --version (funguje, ale potřeba parsovat emoji prefix), importlib.metadata v systémovém Pythonu (nefunguje — nanobot-ai tam není), importlib.metadata s PEP 723 hlavičkou (funguje spolehlivě).
Co fungovalo a proč: PEP 723 shebang (#!/usr/bin/env -S uv run --script) + dependencies = ["nanobot-ai"] zajistí, že skript běží ve venv kde nanobot-ai je nainstalován → importlib.metadata.version("nanobot-ai") vrátí správnou verzi. Žádný subprocess, žádné parsování. Shodné s projekční konvencí.
Ověření: uv run check_nanobot_version.py vrátil Current nanobot version: 0.2.1 a všechny tři zdroje (PyPI, GitHub, Docker) také 0.2.1 → žádná falešná notifikace.
Co zbývá: nic.
2026-05-31 — Skill /plan — plan mode jako v Claude Code
Cíl: Vytvořit nanobot skill /plan, který replikuje chování plan mode z Claude Code: nejdřív task read-only prozkoumat a naplánovat, plán předložit ke schválení, a teprve po explicitním schválení (klidně později) vykonat.
Co jsem zkusil:
- Průzkum — prošel strukturu
skills/(note, keep, deep-research jako vzory): skill =SKILL.mds YAML frontmatterem (name+ víceřádkovýdescriptions triggery) + EN-only markdown tělo. Žádný harness, vše je promptová instrukce. - Ověření subagentů — původně jsem v plánu chybně tvrdil, že nanobot nemá subagenty. Uživatel mě opravil, ověřil jsem v upstreamu: nanobot má builtin
spawntool (spawn.py) — hlavní agent spawne subagenta, ten běží async v pozadí a výsledek vrací přes MessageBus. Lokálně to potvrzují zmínky vworkspace/picoclaw/(spawn, subagent — vše enabled). - Návrh — 4 lineární fáze: Explore (read-only) → Design → zápis plánu do
workspace/plans/<slug>.md→ zastavení a dotaz na schválení/vykonání. Read-only vynuceno promptem (## Rules), schvalovací smyčka přes chat jako náhradaExitPlanMode.spawnjen volitelně pro velký dělitelný průzkum (async povaha by běžné iterativní plánování spíš zdržovala). - Deploy —
rsync -av skills/plan/ nanobot@nanobot.hell:.../skills/plan/, ownershipnanobot:nanobotověřeno. Skilly se čtou při každém tahu bez restartu →/planhned aktivní.
Co fungovalo a proč: Konzistence se stylem note/deep-research (EN-only tělo, triggery CZ+EN, fázová struktura se status zprávami jako deep-research). Lineární default + volitelný spawn odpovídá tomu, že spawn je async — synchronní lineární čtení je pro plánování plynulejší.
Jak vrátit zpět: ssh nanobot@nanobot.hell 'rm -rf ~/.nanobot/workspace/skills/plan' + smazat lokální skills/plan/.
Co zbývá:
- Smoke test přes Telegram/WebUI:
/plan <malý úkol>→ ověřit read-only průzkum, zápis plánu doworkspace/plans/, zastavení s dotazem, a vykonání až po „ano, vykonej" (po „ne" plán zůstane uložený).
2026-05-29 — Instalace nvm + zpřístupnění npx pro nanobot service
Cíl: Zprovoznit npx (a tím clawhub CLI) na serveru nanobot.hell.
Co jsme zkusili:
- Instalace nvm pro uživatele
nanobot:curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash - Instalace Node.js:
nvm install --lts→ nainstaloval se v24.16.0 do/home/nanobot/.nvm/versions/node/v24.16.0/ - nvm přidal inicializaci do
.bashrc— to funguje v interaktivním shellu, ale systemd user service.bashrcnesourcuje
Co fungovalo a proč:
- Přidáno
Environment=PATH=/home/nanobot/.nvm/versions/node/v24.16.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bindo/home/nanobot/.config/systemd/user/nanobot.service - Reload + restart přes
ssh nanobot@nanobot.hell "systemctl --user daemon-reload && systemctl --user restart nanobot"(root přes sudo/machine nefunguje, přímé SSH jako nanobot ano) - Ověřeno:
/proc/<pid>/environobsahuje správnou PATH s nvm cestou
Co zbývá: Při upgradu Node.js přes nvm je potřeba aktualizovat verzi v service souboru ručně.
Jak vrátit zpět: Odstranit řádek Environment=PATH=... ze service souboru, daemon-reload, restart.
2026-05-29 — SQLite konvence + sync workspace/*.md
Cíl: Přidat do AGENTS.md instrukci, že agent má SQLite databáze ukládat do db/*.sqlite.
Co jsem zkusil / udělal:
- Zkoumal, kde žijí instrukce agenta — SSH na server, prošel
/home/nanobot/.nanobot/workspace/. - Zjistil, že správné místo je
AGENTS.md(operační pravidla), neSOUL.md(osobnost) ani skills. - Přidal sekci
## Databases (SQLite)doworkspace/AGENTS.md(anglicky) a pushnu na server. - Při té příležitosti zjistil, že lokální
workspace/*.mdjsou zaostalé — chyběly sekceFaktografická pravidla, Telegram ID,Scheduling,Životní filozofie, apod. - Přepsal všech 5 souborů aktuální verzí ze serveru a commitnul do gitu.
Co fungovalo: Přímý SCP push AGENTS.md na server. Lokální workspace/ jako tracking directory pro server soubory.
Co zbývá: sqlite MCP server v config.json je hardcoded na /home/nanobot/.nanobot/workspace/tmp/test_mcp.db — neodpovídá nové konvenci db/*.sqlite.
2026-05-29 — remind_edit.py: deterministický CRUD pro reminder.yaml
Cíl: Skill remind nechával LLM přímo editovat reminder.yaml — žádná validace cron výrazu, riziko poškozený YAML, potenciální race s remind_send.py (crontab každou minutu).
Co jsem zkusil: Zvažovaná alternativa MCP server — zamítnuta jako overkill (zbytečná vrstva pro jednoduchý YAML CRUD). Zvoleno: CLI Python skript volaný přes exec, stejný pattern jako remind_send.py.
Co fungovalo: Napsán /home/nanobot/.nanobot/workspace/skills/remind/scripts/remind_edit.py (PEP 723, uv run, argparse). Subcommandy list, add --text ... --cron|--at ..., remove --keyword .... Atomický zápis přes .yaml.tmp + os.replace(). Validace: croniter.is_valid() pro cron výrazy, datetime.fromisoformat() pro --at. Všechny výstupy JSON; chyby na stderr + non-zero exit. Aktualizován SKILL.md — workflowy Create/List/Remove teď volají remind_edit.py místo přímé editace YAML.
Verifikace: Prošly testy 1–7 (list baseline, add cron, add at, invalid cron, remove ambiguous, remove unique, cleanup). YAML na serveru po testech obsahuje původní 4 připomínky beze změny.
Co zbývá: —
2026-05-28 — detach skill: odstranění hardcoded chat_id
Cíl: skills/detach/config.json obsahoval soukromé Telegram chat ID přímo v gitu. Skill nebyl generický.
Co jsem zkusil: Přečetl jsem strukturu ~/.nanobot/config.json na serveru a zjistil, že ID je uloženo pod channels.telegram.allowFrom[0].
Co fungovalo: Odstraněn DETACH_CONFIG z tasks-daemon.py, resolve_telegram_chat_id() nyní čte cfg["channels"]["telegram"]["allowFrom"][0] z hlavního configu. skills/detach/config.json smazán z repo i ze serveru.
Co zbývá: —
2026-05-28 — Smazán mrtvý skill deliver-reminder-notifications
Cíl: Uživatel si všiml, že na serveru ve workspace/skills/ leží skill deliver-reminder-notifications z minulé session. Zjistit, proč tam je, a pokud je mrtvý, smazat.
Co jsem zjistil:
- Skill
deliver-reminder-notifications(vytvořen 28. 5. 12:07) je zbytek po staré agentní architektuře připomínek. Volá skriptremind/scripts/remind_check.py, který už neexistuje (vremind/scripts/je jenremind_send.py). - Aktuální doručování běží přes system crontab (
uv run remind_send.pykaždou minutu) přímo přes Telegram Bot API — žádný agent v loopu. Vizremind/SKILL.mdna serveru, který explicitně píše "Do not recreate aremind-checkjob". - Žádný cron ani jiný skill
deliver-reminder-notificationsnetriggeruje.
Co fungovalo:
ssh root@nanobot.hell "rm -rf /home/nanobot/.nanobot/workspace/skills/deliver-reminder-notifications"
Po smazání ve workspace/skills/ zbývají: detach, grill-me, remind.
Jak vrátit zpět: Obnovit adresář se souborem SKILL.md (frontmatter name: deliver-reminder-notifications, popis polling + Telegram delivery v češtině). Ale nemělo by smysl — skript, na který odkazoval, neexistuje a delivery dělá cron.
2026-05-28 — Faktografická pravdomluvnost do SOUL.md, demystifikace memory/
Cíl: Uživatel pozoroval, že nanobot v chatu halucinoval o filmech. Pokusil se to „opravit" tak, že agentovi řekl pravidlo a ten si ho uložil do ~/.nanobot/workspace/memory/film_policy.md. Halucinace pokračovaly. Otázka: jak to opravit správně + obecně zvýšit pravdomluvnost.
Klíčové zjištění (ověřeno ve zdrojáku): Z adresáře memory/ se do system promptu auto-loaduje výhradně MEMORY.md. ContextBuilder ani MemoryStore žádné jiné soubory v memory/ nečte. Tj. memory/film_policy.md byl pro agenta neviditelný — vytvořil ho, ale v dalším tahu už ho neměl v kontextu, takže pravidlo neaplikoval.
Zdroj: /home/nanobot/.local/share/uv/tools/nanobot-ai/lib/python3.13/site-packages/nanobot/agent/{context.py,memory.py} — verze 0.2.0:
context.py:25—BOOTSTRAP_FILES = ["AGENTS.md", "SOUL.md", "USER.md", "TOOLS.md"]memory.py:55,205,229—memory_file = memory_dir / "MEMORY.md",get_memory_context()čte výhradně tento jeden soubor
Pozn. k upstream HEAD: v HKUDS/nanobot master branch už BOOTSTRAP_FILES neobsahuje TOOLS.md (jen AGENTS, SOUL, USER). Naše verze 0.2.0 ho ještě má. Při budoucím upgrade nanobotu to ověřit a případně přesunout obsah TOOLS.md jinam.
Co fungovalo (řešení):
- Nová sekce v SOUL.md —
## Faktografická pravidla, vložená před## Styl výstupuna serveru. Obsah: imperativní pravidlo „když nevím, řeknu to" + „u faktografických dotazů nejdřív dohledám tooly, pak odpovídám" + „halucinace = vážná chyba, ne kosmetika". Patch přes Python in-place na serveru (idempotence ověřenaassert "## Faktografická pravidla" not in text), žádný restart nepotřeba (bootstrap soubor). - Smazán
memory/film_policy.md— nebyl čten, jen mátl. - Knowledge.md doplněn o dvě nové sekce: „Co se auto-loaduje do system promptu (verze 0.2.0)" + tabulka „K čemu slouží jednotlivé workspace soubory" (SOUL/AGENTS/USER/TOOLS/MEMORY/HEARTBEAT). Stará minimální sekce „Workspace bootstrap soubory se čtou každý tah" nahrazena bohatší variantou; sekce „Workspace vzniká při prvním spuštění agenta" ponechána beze změny.
Proč SOUL.md a ne AGENTS.md / skill:
- AGENTS.md drží procesní pravidla (jaký tool kdy) — pravdomluvnost je hodnota, ne proces.
- Always-on skill pro jedno pravidlo je overkill. Skilly dávají smysl, když potřebuje izolaci (own helper scripts, modulární distribuce). Sémanticky horší než SOUL pro hodnotové pravidlo.
- MEMORY.md by Dream při konsolidaci mohl přepsat — nehodí se na trvalá pravidla chování.
Co jsem zkusil mimo úspěšnou cestu:
- Explore agent na začátku trval na konkrétních line numbers v
/tmp/nanobot-src/...ještě před tím, než tam upstream byl klonovaný — fabulace zdroje, kterou jsem musel přebít přímým ověřením. Jasná ilustrace toho, proč user chce pravdomluvnost: i podagent halucinoval citace zdroje. Pravidlo „neopakuj číslo řádku, dokud jsi ho neviděl" by se hodilo i na můj vlastní workflow, ne jen na nanobota. - Místně bylo
BOOTSTRAP_FILES = [AGENTS, SOUL, USER]v upstream HEAD — málem jsem usoudil, žeTOOLS.mdse nečte. Ověření verze na serveru (nanobot --version→ 0.2.0,grep BOOTSTRAP_FILESv nainstalovaném wheelu) odhalilo divergenci verzí.
Jak to vrátit zpět:
- SOUL.md:
ssh nanobot@nanobot.hell 'python3 -c "from pathlib import Path; p=Path.home()/\".nanobot/workspace/SOUL.md\"; t=p.read_text(); import re; p.write_text(re.sub(r\"## Faktografická pravidla.*?(?=## Styl výstupu)\", \"\", t, flags=re.S))"' - film_policy.md: obnovit z paměti (krátký, obsah je teď v history.md SOUL.md patchi nebo v původní git komitové historii local repo, pokud byl).
Co zbývá:
- Live verifikace v Telegramu — dotaz na film, sledování
journalctl -u nanobot.service -fnaweb_search/web_fetchtool call před odpovědí. Cross-test s netriviálním dotazem (ověřit „nevím" místo improvizace). Negativní kontrola s in-context dotazem (např. „jaký mám preset?") — nesmí spustit zbytečné web search.
2026-05-28 — Pilot mattpocock skillu grill-me
Cíl: Ověřit, jestli Claude Code skilly (Anthropic Skills format) jdou pustit v nanobotovi bez konverze. Jako kanárek vybrán grill-me z nicecode/plugins/mattpocock — nejmenší možná povrchová plocha (10 řádků, žádné helper skripty, žádné @-reference, žádné Claude-Code-specific tooly v těle).
Co fungovalo:
- Formát
SKILL.md(YAMLname+description+ markdown tělo) je identický s nanobot skill formátem. Kopie 1:1, žádná konverze. - Deploy přímo na server jedním SSH+curl one-linerem (
ssh nanobot@nanobot.hell→mkdir -p ~/.nanobot/workspace/skills/grill-me→curl -sSfL <raw github URL> -o SKILL.md). Server-to-GitHub, lokálně se nic neukládá. - Restart nanobot.service není potřeba — bootstrap soubory ve
workspace/skills/se čtou každý tah (knowledge.md "Workspace bootstrap soubory se čtou každý tah"). - Uživatel ověřil v chatu: agent na trigger frázi začal pokládat otázky jednu po druhé s recommended answers, místo aby plán rovnou implementoval. Chování přesně podle popisu skillu.
Co jsem zkusil mimo úspěšnou cestu:
- WebFetch na
diagnose/SKILL.mdpři průzkumu vrátil výstup s fake<system-reminder>tagem uvnitř markdown těla (prompt-injection patrně v Anthropic-fetch pipeline). Zaznamenáno v knowledge.md jako gotcha pro budoucí mattpocock skilly — markdown těla je nutné prohlédnout před deployem, protože nanobot je čte do system contextu. WebFetchcelkově nedával užitečné odpovědi na otázky typu „obsahuje tento soubor X?" — vracel jen echo dotazu. Přepnul jsem nacurl | greppřes Bash a dostal jednoznačné odpovědi.
Manifest .claude-plugin/plugin.json se nekopíruje — je Claude-Code-specific metadata pro marketplace, nanobot ho ignoruje. Přenositelná je jen složka skills/.
Jak to vrátit zpět: ssh nanobot@nanobot.hell 'rm -rf ~/.nanobot/workspace/skills/grill-me'. Žádné side effecty mimo tu složku.
Co zbývá: Zatím nic — uživatel zvolil pilotně jen jeden skill, další (caveman, diagnose, tdd, triage, zoom-out, grill-with-docs, improve-codebase-architecture) zatím neinstalovat. Plán nasazení existuje v ~/.claude/plans/jak-by-sel-pouzit-agile-thimble.md — při rozšíření projít každý další skill na (a) Claude-Code-specific tool refs, (b) prompt-injection v markdown těle. to-issues a to-prd vyžadují gh CLI a GitHub kontext — na serveru pravděpodobně nepoužitelné bez adaptace.
2026-05-28 — Reminder logy do log/ a čitelnější formát
Cíl: Logy /remind skiluu byly v rootu workspace (smetí v hlavním adresáři), formát byl ISO+02:00 \t text (tabulátor, timezone suffix nechtěný).
Co fungovalo:
LOG_DIR = WORKSPACE / "log"vskills/remind/scripts/remind_send.py,LOG_DIR.mkdir(parents=True, exist_ok=True)před zápisem.- Formát změněn na
datetime.now(TZ).replace(tzinfo=None).isoformat(timespec="seconds")+ mezera + text +\n→YYYY-MM-DDTHH:MM:SS <text>. TZ zůstává Prague, jen se nepíše do řetězce. - Crontab pod nanobot userem přepsán:
>> /home/nanobot/.nanobot/workspace/log/reminder_cron.log 2>&1. Souborreminder_cron.logzůstává — chytá stdout/stderr crontabu pro debug (za zdravého běhu prázdný). - Stávající
reminder.log(8 řádků) přepsán do nového formátu a přesunut dolog/;reminder_cron.log(0 B) jen přesunut. - Ověřeno: cron tick
(nanobot) CMD (uv run …remind_send.py >> /home/nanobot/.nanobot/workspace/log/reminder_cron.log 2>&1)v journalu, exit 0, log/ vznikl, root workspace už neobsahuje*.log. TOOLS.mdaskills/remind/SKILL.mdaktualizovány na novou cestu i formát; agent dostane upozornění při příštím tahu (always-on bootstrap).
Proč dva logy: log/reminder.log = aplikační (úspěšná doručení, čte agent). log/reminder_cron.log = zachytávka stdout/stderr crontabu (>> ... 2>&1) — bezpečnostní síť pro tracebacky / chyby uv. Funkce se nepřekrývají.
Co selhalo / zákoutí:
- První patch
remind_send.pyjsem zkusil podsudo -u nanobot→PermissionError. Soubor byl ownedroot:root(vytvořen v předchozí session přes SSH jako root). Fix: patch jako root +chown nanobot:nanobotpo editaci. - Reformat stávajícího
reminder.logpřes „chytrý" Python jednořádkový regex (ts.split("+")[0].split("-0")[0]) zmrvil obsah na samé2026 <text>—-0matchlo už-05v datu. Fix: přepsat soubor staticky známým obsahem (8 řádků jsem stejně měl před očima z předchozíhocat). - Manuální dry-run
sudo -u nanobot uv run …selhal dvakrát: nejdřívuv: command not found(PATH bez~/.local/bin), pakfailed to open /root/uv.toml(cwd zůstal/root). Funguje ažcd /home/nanobot && sudo -Hu nanobot bash -lc "uv run …". Cron tohle neřeší — má vlastníHOMEaPATHv hlavičce crontabu a běží z domovského adresáře nanobota.
Jak to vrátit zpět: v remind_send.py vrátit LOG_FILE = WORKSPACE / "reminder.log" a starý zápis s \t + tz, v crontabu vrátit cestu bez log/, soubory přesunout zpět.
2026-05-27 — Spam "Output was empty": skutečná příčina + přechod /remind mimo agenta
Cíl: Pochopit, proč chodí na Telegram zprávy "(Output was empty — no message sent.)" / "(Výstup byl prázdný…)" — a ne každou minutu, ale jen sem tam a pokaždé jinak formulované. Předchozí fix ("exit silently" v promptu) nestačil.
Skutečná příčina (oprava dřívějšího závěru):
- Prompt
"The scheduled time has arrived. Deliver this reminder to the user now…"není z jobs.json — je natvrdo ve zdrojákunanobot/cli/commands.py:on_cron_job.payload.messagez jobs.json se jen přilepí zaReminder:. Takže každý cron job dostane konfliktní zadání (kód přikazuje doručit, můj text říká mlčet) → agent improvizuje meta-odpověď. - Cron job VŽDY jede přes agenta (
agent.process_direct) — neagentní typ jobu neexistuje. Agent komentuje své mlčení místo aby skutečně mlčel. - Doručení hlídá druhý LLM
evaluate_response(nanobot/utils/evaluator.py), který je fail-open: při jakékoli chybě nebo když model nevrátí tool-call →return True(doruč). V journalu ~20×/den"no tool call returned, defaulting to notify". - → Únik nastane jen v minutách, kdy evaluator LLM selže. Nedeterministické a model-závislé (zhoršilo se po přepnutí na slabší Ollama-cloud modely) → "jen sem tam". Text je pokaždé čerstvě generovaný → "pokaždé jiná zpráva".
Co fungovalo (řešení — Design A, mimo agenta):
- Nový
skills/remind/scripts/remind_send.py: čte reminder.yaml, při shodě posílá přímo přes Telegram Bot API (token z~/.nanobot/config.json→channels.telegram.token), deduplikuje přes.reminder_state.json(každý fire 1×), loguje doreminder.log(ISO-čas \t text). Žádný LLM, žádný nanobot proces. Stdliburllib(deps jen croniter+pyyaml). - Odebrán nanobot cron job
remind-check(id 1a7bb634): stop service → filtr v jobs.json → start. Po restartu se nevrátil. Zálohacron/jobs.json.bak-*. - Crontab pod uživatelem nanobot (
crontab -l):* * * * * uv run …/remind_send.py >> reminder_cron.log 2>&1, sHOME+PATHv hlavičce (cron má holé prostředí). Cron daemoncron.servicena hostu běží. Ověřeno:(nanobot) CMD (uv run …remind_send.py)v journalu každou minutu, log prázdný (běží čistě). TOOLS.md(always-on bootstrap) dostal ukazatel nareminder.log;SKILL.mdpřepsána architektura; smazán starýremind_check.py.
Co konkrétně přibylo do system promptů (always-on bootstrap = každý tah agenta):
-
TOOLS.md— nová sekce na konci souboru (řádky 51+):reminder.log — doručené připomínky
Odeslané připomínky se logují do
reminder.logv rootu workspace (append-only, formátISO-čas \t text). Posílá je systémový cron uživatele nanobot (skills/remind/scripts/remind_send.py) přímo přes Telegram, mimo agenta. Když se uživatel ptá na minulé/dnešní připomínky („připomněl jsi mi…?", „co dnes přišlo?"), přečti tento soubor.(Pozn.: 28.5. tato sekce dostala druhý revize na
log/reminder.log+ nový formát — viz entry olog/.) -
skills/remind/SKILL.md— sekce Architecture přepsána na popis systémového crontabu +remind_send.py+ Bot API + dedup + audit log. Sekce Cron Tool Usage přejmenována na Cron Tool — NOT used for reminders s explicitním zákazem znovu vytvořitremind-checkjob přescrontool (jinak by se vrátil starý fail-open agentní problém). Reference v dokumentaci přepnuta zremind_check.py→remind_send.py. -
Token + API ověřeno přes
getMe; živý testsendMessagedoručen (uživatel potvrdil "prislo").
Jak to vrátit zpět: obnovit cron/jobs.json.bak-* + restart service (vrátí agentní job remind-check); crontab -r jako nanobot (smaže crontab).
Co zbývá: Ověřit reálné doručení ostré připomínky (zítra 9:00, příp. 9:30 čt). Zvážit nahlášení fail-open evaluatoru upstream (HKUDS/nanobot).
2026-05-27 — Průzkum nanobot skill systému a Dream procesoru
Cíl: Zjistit jak se instalují nové skilly (konkrétně clawhub.ai/pskoett/self-improving-agent) a jestli má smysl ho nasadit.
Co jsem zjistil:
-
Skill systém nanobot: Skill = složka
~/.nanobot/workspace/skills/<name>/se souboremSKILL.md(YAML frontmatter + markdown instrukce). Žádnýinstallpříkaz neexistuje — skill se vytvoří ručně. Bootstrap soubory se čtou při každém tahu → restart není potřeba. -
Clawhub.ai je pro jiný systém. Clawhub / OpenClaw není nanobot. Instalace přes
clawdhub installneboopenclaw skills installjsou příkazy pro ten jiný ekosystém. Skilly z clawhub se musí ručně adaptovat pro nanobot. -
Dream procesor (
nanobot/agent/memory.py:Dream) dělá přesně to, co má self-improving-agent skill — automaticky, každé 2 hodiny:- Fáze 1: Plain LLM call projde
history.jsonl, hledá fakta →MEMORY.md, korekce →SOUL.md/USER.md, opakující se workflow →[SKILL]kandidáti - Fáze 2: AgentRunner s
read_file/edit_filetools chirurgicky zapíše změny, umí sám vytvářet nové skilly (write_filedoskills/<name>/SKILL.md), deuplikuje, detekuje stale content přes git blame
- Fáze 1: Plain LLM call projde
-
Self-improving-agent skill je z 80 % redundantní s Dream. Přidal by jen: explicitní error log s formátem
ERR-YYYYMMDD-XXX, feature request tracking, okamžité zachycení (Dream čeká 2h). Nasazen jako experimentálnískills/self-improving-agent/SKILL.mdlokálně (zatím nedoručen na server). -
Modely pro dreaming: DeepSeek V3 > Qwen 3.6+ > Kimi K2 ≈ GLM-5.1 > MiniMax M2 pro structured editing / instruction following. GLM-5.1 nijak nevyniká. Dream používá stejný model jako aktivní preset.
Výsledek: Skill nebylo potřeba nasazovat — Dream ho pokrývá. Lokální soubor skills/self-improving-agent/SKILL.md existuje pro případné budoucí použití.
2026-05-27 — Oprava spamu "Žádné připomínky" z cron jobu remind-check
Cíl: Zastavit opakované Telegram zprávy "Žádné připomínky k doručení." chodicí každou minutu.
Co jsem zkusil:
- Zkontroloval hooks v
.claude/settings.jsona~/.claude/settings.json— nenašel nic relevantního. CronListv harness — žádné naplánované joby tady.- Hledal skripty s "telegram"/"remind" na serveru — našel
/home/nanobot/.nanobot/workspace/cron/jobs.json. - V
jobs.jsonidentifikoval jobremind-check(id1a7bb634) s"expr": "* * * * *"(každá minuta). - Přečetl
remind_check.py— skript správný, tiskne jen skutečné připomínky. - Problém: prompt agenta říkal "If output is non-empty, forward" — LLM to ignoroval a posílal "Žádné připomínky" jako potvrzení.
Co fungovalo a proč:
- Upravil
payload.messagevjobs.jsonpřes Python in-place editor na serveru — přidal explicitní instrukci "If the output is EMPTY, do NOT send any message — exit silently." - Restart nanobot služby:
su - nanobot -s /bin/bash -c 'XDG_RUNTIME_DIR=/run/user/$(id -u nanobot) DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u nanobot)/bus systemctl --user restart nanobot.service'
Slepá ulička:
kill -HUP <pid>na gateway proces — nanobot se nerestartoval, ale skončil a nenaskočil zpět. Poté bylo třeba ho ručně nastartovat výše uvedeným příkazem.su - nanobot -c 'systemctl --user restart ...'bezXDG_RUNTIME_DIRaDBUS_SESSION_BUS_ADDRESSselhalo s "bus not defined".
Co zbývá: Ověřit za pár minut, že spam ustal.
2026-05-27 — Doladění /remind skilu: path bug + jobs.json neuloženy
Cíl: Dokončit opravu /remind skilu — ověřit end-to-end doručení na Telegram.
Co jsem zkusil / Co fungovalo:
Skript remind_check.py vracelo prázdný výstup i po přepsání. Přímé testování odhalilo path bug: Path(__file__).parent.parent.parent z .../skills/remind/scripts/remind_check.py vede na .../workspace/skills/ — reminder.yaml tam neexistuje → skript tiše skončil na if not REMINDER_YAML.exists(): return. Oprava: 4 levely nahoru (parent.parent.parent.parent).
Změny v jobs.json z předchozí session se neuložily — soubor stále obsahoval remind_runner.py a channel: websocket na ephemeral session ID. Opraveno přes Python in-place edit na serveru + restart service.
Výsledek: Cron job spouští uv run remind_check.py → stdout → nanobot evaluate_response: should_notify=True → doručeno na Telegram. Ověřeno uživatelem.
Co zbývá: Testovací záznam TEST každou minutu smaže uživatel přes chat.
2026-05-27 — Oprava /remind skilu
Cíl: Diagnostika a oprava nefunkčního /remind skilu — notifikace se neodesílaly správně, LLM občas vytvářel cron joby místo záznamu do reminder.yaml, Telegram polyká /remind příkazy.
Co jsem zkusil / Co fungovalo:
Průzkum ukázal dva Python skripty se odlišnou architekturou:
remind_runner.pybyl aktivní (cron job ho spouštěl), ale špatný — posílal Telegram přímo přes API (obcházel nanobot), nemělatfield support, měl hardcoded UTC+2remind_check.pyměl správnou architekturu (stdout → nanobot → user), podporovalatfield, ale měl UTC timezone místo Prague
Provedené opravy:
remind_check.pypřepsán: Prague timezone přeszoneinfo, zjednodušen z ~70 na ~30 řádkůremind_runner.pysmazáncron/jobs.json— jobremind-checkpřepnut naremind_check.py+ delivery kanál změněn na Telegram (byl websocket session)SKILL.md— odstraněndonefield ze schématu, přidány sekce "Telegram — jak zadat příkaz" a "Remind vs. cron tool"AGENTS.md+TOOLS.md— přepsána sekce o reminderech s jasným rozlišením: osobní notifikace → reminder.yaml, background úlohy → cron toolreminder.yamlvyčistěn: odstraněnydone: falsezáznamy a zastaralý testovací záznam
Telegram problém: /remind text v Telegramu = bot příkaz, ne text. Řešení: psát přirozeně (připomeň mi...). SKILL.md má triggery na tato slova, ale uživatel o tom nevěděl — přidána sekce do SKILL.md.
Co zbývá: Ověřit v praxi že notifikace z Telegramu dorazí správně.
2026-05-26 — Přepracování CLAUDE.md a vytvoření šablony
Cíl: Zjednodušit a zpřehlednit CLAUDE.md pro nanobot projekt + vytvořit čistou šablonu pro reuse v dalších projektech.
Změny (první vlna):
- CLAUDE.md zkráceno z ~84 na ~46 řádků — pravidla přepsána do kompaktní tabulky + odrážkových sekcí
- Přidán
plans.mddo tabulky tracking souborů (byl osiřelý, v CLAUDE.md nezmiňovaný) - Sekce commitu zhuštěna na 4 odrážky (byl zbytečně rozvláčný)
- Odstraněn slug z instrukce pro auto-memory override (byl křehký a projekt-specifický)
- Záložní šablona uložena do
/home/lachtan/CLAUDE.template.md
Změny (druhá vlna — workflow zkoušení/ladění):
- Distilace history → knowledge: vyřešené netriviální chyby se z deníku vytahují jako jednořádkové problém → příčina → fix do
knowledge.mds odkazem zpět. Automaticky, hned po vyřešení. - Otevřené otázky mají domov v
todo.md(prefixotázka:); po uzavření odpověď →knowledge.md. - Zamítnuté možnosti se zaznamenávají do
knowledge.md(co a proč zavrženo, ať se netestuje znovu). - Struktura záznamu v history.md kodifikována: Cíl / Co jsem zkusil / Co fungovalo a proč / Co zbývá + rollback u destruktivních zásahů.
- Secrets přidány do NEDĚLEJ (nikdy do tracking souborů — jsou v gitu).
- Stejné změny promítnuty do šablony.
2026-05-26 — Nastavení locale en_US.UTF-8 na nanobot.hell
Problém: SSH session do root@nanobot.hell házelo opakované warningy:
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory
locale: Cannot set LC_CTYPE to default locale: No such file or directory
Diagnóza:
locale -ana serveru ukázal jenC,C.utf8,POSIX—en_US.UTF-8nebyl vygenerován./etc/locale.genbyl bez aktivních řádků (vše zakomentované)./etc/default/localemělLANG="C".- SSH klient přitom přes
AcceptEnv LANG LC_*posílalLC_ALL=en_US.UTF-8atd. → server warning.
Řešení (proběhlo na serveru):
Nejdřív manuálně přes locale-gen + update-locale:
sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
Pak na žádost uživatele přepnuto na dpkg-reconfigure (uloží volbu do debconf DB, takže příští reconfigure si ji pamatuje):
echo 'locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8' | debconf-set-selections
echo 'locales locales/default_environment_locale select en_US.UTF-8' | debconf-set-selections
rm -f /etc/locale.gen
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure -f noninteractive locales
Ověření: Po fixu žádné setlocale warningy. /etc/default/locale obsahuje LANG=en_US.UTF-8 a LC_ALL=en_US.UTF-8. Locale en_US.UTF-8 je v locale -a.
Poznámka: V interaktivní session se stále zobrazuje LANG=C — to je hodnota poslaná SSH klientem (sshd má AcceptEnv LANG LC_* COLORTERM NO_COLOR), nikoli problém serveru. Defaulty serveru jsou správně nastavené.
2026-05-26 — Založení uživatele nanobot a instalace uv
Cíl: Pod uživatelem nanobot provozovat nanobot agenta. SSH klíče sdílené s rootem, uv nainstalované do user-local prefixu.
Provedeno na serveru nanobot.hell:
-
Nainstalován
curl(chyběl v základní instalaci Debian 13 trixie):apt-get update apt-get install -y curlStáhlo se i
libcurl4t64jako závislost. -
Vytvořen uživatel
nanobot(UID 1000, GID 1000) s home/home/nanobota shellem/bin/bash. Heslo zůstalo locked (!v shadow) — login jen přes SSH klíče:useradd -m -s /bin/bash nanobot install -d -m 700 -o nanobot -g nanobot /home/nanobot/.ssh install -m 600 -o nanobot -g nanobot /root/.ssh/authorized_keys /home/nanobot/.ssh/authorized_keys -
Ověřeno přihlášení
ssh nanobot@nanobot.hellstejným klíčem jako root — funguje. -
Pod uživatelem
nanobotnainstalovánouvpřes oficiální installer (uživatel spustil ručně, automatický pokus přescurl … | shbyl zablokován harness klasifikátorem na sdíleném remote hostu):curl -LsSf https://astral.sh/uv/install.sh | shVýsledek:
uv 0.11.16 (x86_64-unknown-linux-gnu)nainstalované do/home/nanobot/.local/bin/{uv,uvx}. Pro načtení do PATH je potřebasource ~/.local/bin/env(nebo nová shell session s upraveným.profile/.bashrc).
Ověření: ssh nanobot@nanobot.hell '~/.local/bin/uv --version' → uv 0.11.16.
2026-05-26 — Časová zóna Europe/Prague a NTP na nanobot.hell
Cíl: Přepnout timezone z Etc/UTC na Europe/Prague a aktivovat NTP.
Stav před:
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: inactive
Provedeno:
timedatectl set-timezone Europe/Prague
timedatectl set-ntp true
Stav po:
Local time: Tue 2026-05-26 18:08:44 CEST
Time zone: Europe/Prague (CEST, +0200)
System clock synchronized: yes
NTP service: inactive
Poznámka k NTP: systemd-timesyncd zůstává inactive záměrně — jednotka má ConditionVirtualization=!container, a protože nanobot.hell běží jako kontejner (LXC), service se přeskočí. Čas se dědí z hosta a System clock synchronized: yes to potvrzuje. V kontejneru tedy nemá smysl NTP klienta spouštět; pokud by bylo potřeba nezávislé synchronizace, musela by se na hostiteli zapnout odpovídající podpora (a/nebo se kontejneru povolit CAP_SYS_TIME).
2026-05-26 — Instalace nanobot-ai a zprovoznění Telegramu + WebUI
Cíl: Nainstalovat nanobot-ai pod uživatelem nanobot na nanobot.hell, napojit OpenRouter (model Kimi K2.6) a zprovoznit dva kanály — Telegram bot a WebUI.
Provedeno:
-
uv tool install nanobot-ai(verze 0.2.0) — uv stáhl 114 balíčků, vytvořil izolovaný venv v/home/nanobot/.local/share/uv/tools/nanobot-ai/, exposed CLI binárku/home/nanobot/.local/bin/nanobot. -
Wizard
nanobot onboard --wizardpřeskočen — schéma configu zdocs/chat-apps.md+docs/websocket.md+docs/deployment.mdna GitHubu, config.json zapsán ručně. -
~/.nanobot/config.json(mode 600, obsahuje secrets):providers.openrouter.apiKey— OpenRouter klíčagents.defaults.provider=openrouter,model=moonshotai/kimi-k2.6channels.telegram—enabled, bot token,allowFrom: ["8826147089"](whitelist Telegram user ID)channels.websocket—enabled,host: 0.0.0.0, statickýtoken(32B hex)gateway.host: 0.0.0.0— bind všech rozhraní (jinak gateway odmítne startnout swebsocket.host: 0.0.0.0bez tokenu)
-
Test LLM:
nanobot agent --message "..."přes OpenRouter odpověděl modelemmoonshotai/kimi-k2.6. Workspace~/.nanobot/workspacese vytvořil při prvním spuštění (AGENTS.md, USER.md, SOUL.md, HEARTBEAT.md, TOOLS.md, memory/, git store). -
Test Telegramu:
nanobot gateway --verbosev popředí → bot@lachran_nanobotse připojil (polling mode). Uživatel poslal zprávu v Telegramu, bot odpověděl ("Běžím na modelu Moonshot AI Kimi K2.6. 🐈"). LLM usage: prompt=6464, completion=62, latency ~5.3s. -
Test WebUI:
curl http://nanobot.hell:8765/→ HTTP 200, HTML SPA znanobot/web/dist/index.html. WebUI HTML i WebSocket auth endpoint jsou na stejném portu 8765 (gateway zdraví zvlášť na 18790).
Otevřené: systemd unit (user-level), enable lingering, restart test.
Hraniční zjištění:
- WebUI HTML je v
~/.local/share/uv/tools/nanobot-ai/lib/python3.13/site-packages/nanobot/web/dist/— součást wheelu, není to externí asset. - README projektu odkazuje na
docs/configuration.md, ale schémawebsocketchannelu v něm chybí — k poznání klíčůhost/port/path/token/allowFrom/streaming/websocketRequiresTokenjsem se dostal přesdocs/websocket.mdadocs/deployment.md. nanobot statusukázalOpenAI Codex: ✓ (OAuth)aGithub Copilot: ✓ (OAuth)přestože nejsou v configu — patrně automatická detekce systémových OAuth tokenů. Žádný efekt na běh, ignoruji.- Port 8765 je teď otevřený na všech rozhraních
nanobot.hell— chráněn statickým bearer tokenem. Až nasadíme nginx reverse proxy nanano.hell.cz, vrátíme bind na127.0.0.1.
2026-05-26 — modelPresets: Kimi + Minimax + Qwen
Cíl: Mít víc modelů na výběr — Kimi K2.6 jako default, Minimax M2.7 a Qwen 3.7 Max jako alternativy.
Zjištění: Nanobot nepodporuje víc pojmenovaných agentů, ale modelPresets — pojmenované sady (provider, model), mezi kterými se přepíná za běhu /model <preset> v chatu (Telegram i WebUI). Jeden agent, několik presetů.
Provedeno:
-
Nejnovější Qwen na OpenRouteru zjištěn dotazem na
/api/v1/models→qwen/qwen3.7-max(flagship Qwen 3.7 série). -
~/.nanobot/config.jsonrozšířen o:"agents": { "defaults": { "modelPreset": "kimi" } }, "modelPresets": { "kimi": { "provider": "openrouter", "model": "moonshotai/kimi-k2.6" }, "minimax": { "provider": "openrouter", "model": "minimax/minimax-m2.7" }, "qwen": { "provider": "openrouter", "model": "qwen/qwen3.7-max" } } -
Gateway restartován.
nanobot statuspotvrdilModel: moonshotai/kimi-k2.6 (preset: kimi). Startup log:Runtime model switched for next turn: moonshotai/kimi-k2.6 -> moonshotai/kimi-k2.6(preset resolver běží na začátku každého turn).
Použití: v chatu napsat /model minimax nebo /model qwen — od dalšího tahu se přepne. /model bez argumentu vrátí aktuální preset.
2026-05-26 — user-level systemd služba nanobot.service
Cíl: Aby nanobot gateway běžel perzistentně (přežil odhlášení uživatele i restart serveru), bez root práv pro vlastní službu.
Volba: user-level systemd unit (~/.config/systemd/user/) místo system-level. Důvod: služba sahá jen do home uživatele nanobot, není důvod ji mít v /etc/systemd/system/ s root vlastnictvím. Lingering vyřeší to, že user manager (user@1000.service) běží nezávisle na přihlášení.
Provedeno:
-
Lingering povolen (vyžaduje root):
ssh root@nanobot.hell 'loginctl enable-linger nanobot'loginctl show-user nanobotpo fixu:Linger=yes. Vytvoří se/run/user/1000/při bootu automaticky, user manager je vuser@1000.service. -
Unit file
/home/nanobot/.config/systemd/user/nanobot.service:[Unit] Description=nanobot AI gateway (Telegram + WebUI) After=network-online.target [Service] Type=simple ExecStart=/home/nanobot/.local/bin/nanobot gateway Restart=on-failure RestartSec=5 Environment=LANG=en_US.UTF-8 Environment=LC_ALL=en_US.UTF-8 [Install] WantedBy=default.targetAfter=network-online.targetje system unit — pro user-scope to nemá tvrdou závislost, ale ordering systemd respektuje, dokud lingering startuje user manager po network-online. -
Aktivace (přes SSH, kde
XDG_RUNTIME_DIRneexistuje v non-login session — nutno nastavit ručně):ssh nanobot@nanobot.hell 'XDG_RUNTIME_DIR=/run/user/1000 systemctl --user daemon-reload \ && systemctl --user enable --now nanobot.service'Symlink
default.target.wants/nanobot.service→ enabled. -
Ověření:
systemctl --user status nanobot.serviceukázalactive (running), Main PID 5657. Endpointy: WebUIhttp://nanobot.hell:8765/→ HTTP 200, healthhttp://nanobot.hell:18790/health→{"status":"ok"}, Telegram bot@lachran_nanobotpřipojený.
Logování: journalctl --user -u nanobot.service -f (nutno mít XDG_RUNTIME_DIR=/run/user/1000 v env, pokud se přihlašuju non-login SSH session).
Otevřené: test, že služba přežije reboot serveru.
2026-05-26 — Reboot test: nanobot.service přežije restart serveru
Cíl: Potvrdit, že lingering + WantedBy=default.target opravdu nastartuje gateway i po cold rebootu, ne jen při interaktivním přihlášení.
Provedeno:
- Pre-reboot
boot_idzaznamenán (fe922ca4-...). ssh root@nanobot.hell 'systemctl reboot'— SSH session dropped (čekáno).- Polling
ssh -o ConnectTimeout=3v 3-sec intervalech, server zpátky po ~15 s, novýboot_id(3d20399d-...),uptime -p=up 0 minutes,systemctl is-system-running=running. nanobot.servicestatus:active (running) since 18:36:13, Main PID 233 (nízké PID = startovalo brzy v bootu, hned jak naběhluser@1000.service).- Journal potvrdil, že telegram i websocket channel naběhly bez chyby.
- Externí ověření z mé strany:
curl http://nanobot.hell:8765/→ HTTP 200 (WebUI),curl http://nanobot.hell:18790/health→{"status":"ok"}.
Závěr: Lingering + user-level systemd unit funguje přesně jak má — žádný manuální zásah po rebootu není potřeba.
2026-05-26 — Swap qwen presetu na qwen3.6-plus
Změna: Uživatel chtěl místo qwen3.7-max použít "qwen-plus". Čistý qwen-plus slug na OpenRouteru neexistuje — dvě "plus" varianty:
qwen/qwen3.5-plus-20260420— multimodal (text/image/video), 1M contextqwen/qwen3.6-plus-04-02— hybridní lineární attention + sparse MoE
Zvolen novější qwen/qwen3.6-plus-04-02 (vyšší verze + novější architektura).
Provedeno:
~/.nanobot/config.jsonupraven přes Python in-place (zachoval ostatní klíče):modelPresets.qwen.model→qwen/qwen3.6-plus-04-02.systemctl --user restart nanobot.service— serviceactive, oba kanály naběhly bez chyby.
Poznámka: done.md záznam o "Qwen 3.7 Max" upraven přímo (model je implementační detail dokončeného úkolu "přidej Qwen jako alternativu"), aby done.md odráželo realitu. History je append-only, tady je proto kompletní stopa.
2026-05-26 — Ollama na nvidia.hell jako druhý provider + přejmenování presetů
Cíl: Vedle OpenRouteru zapojit lokální Ollama instanci, která běží na nvidia.hell:11434. Ta hostuje i :cloud-tagované modely (Ollama si je tahá ze své cloud služby na vyžádání, takže lokální endpoint nabízí nejen lokálně stažené modely, ale i cloudové).
Korekce hostname: V dřívějším todo bylo nvidila.hell — překlep podle uživatelovy zprávy. Správně je nvidia.hell (192.168.4.45). nvidila.hell neexistuje v DNS.
Zjištění Ollama modelů: curl http://nvidia.hell:11434/api/tags — 57 modelů. Z toho cloud-tagované (:cloud suffix, ne -cloud):
deepseek-v3.2:cloud, gemma4:31b-cloud, glm-5.1:cloud, gpt-oss:120b-cloud,
kimi-k2.5:cloud, kimi-k2.6:cloud, minimax-m2.7:cloud,
nemotron-3-super:cloud, qwen3.5:cloud
Pro nás zajímavé: kimi-k2.6:cloud (nejvyšší Kimi) a minimax-m2.7:cloud (jediný dostupný Minimax).
Provedeno:
-
Přidán provider
ollamasapiBase: http://nvidia.hell:11434(klíčapiBase, nikolibaseUrl— vizdocs/configuration.md). -
modelPresetspřejmenovány a doplněny (uživatel chtěl rozlišit Ollama vs. OpenRouter v názvu):preset provider model kimi-k2.6-openrouter(default)openrouter moonshotai/kimi-k2.6minimax-m2.7-openrouteropenrouter minimax/minimax-m2.7qwen-3.6-plus-openrouteropenrouter qwen/qwen3.6-plus-04-02kimi-k2.6-ollamaollama kimi-k2.6:cloudminimax-m2.7-ollamaollama minimax-m2.7:cloud -
agents.defaults.modelPresetpřesměrován nakimi-k2.6-openrouter(původněkimi, nyní s novým názvem). Default zůstal stejný (Kimi K2.6 přes OpenRouter). -
systemctl --user restart nanobot.service— serviceactive, oba kanály naběhly.nanobot statuspotvrdilModel: moonshotai/kimi-k2.6 (preset: kimi-k2.6-openrouter). -
Konektivita ověřena:
curl http://nvidia.hell:11434/api/tagsznanobot.hellprojde,kimi-k2.6:cloudk dispozici. Reálné odeslání zprávy přes Ollama preset zatím netestováno — proběhne automaticky, jakmile někdo v Telegramu/WebUI udělá/model kimi-k2.6-ollama.
Edit configu in-place: Použit python3 -c na serveru (json.loads → mutate → json.dumps), aby se zachovaly ostatní klíče (token, telegram, gateway, websocket). Předtím jsem to dělal přepsáním celého souboru přes heredoc — bezpečnější, ale upovídanější.
2026-05-26 — Oprava Ollama provideru: /v1 suffix v apiBase
Problém: Uživatel hlásil, že ollama presety nefungují. Test přes nanobot agent --message ... --logs s dočasným přepnutím default presetu na kimi-k2.6-ollama:
LLM returned error: Error: 404 page not found
Streamovaný startup log předtím říkal Runtime model switched for next turn: kimi-k2.6:cloud -> kimi-k2.6:cloud, takže slug modelu byl správný a request odešel — Ollama jen na cestě, kterou nanobot zavolal, vrátil 404.
Diagnóza: Přímý curl na http://nvidia.hell:11434/api/chat s payloadem {"model":"kimi-k2.6:cloud","messages":[...]} fungoval (model odpověděl). Z toho plyne, že nanobot nevolá Ollama-native /api/chat, ale OpenAI-kompatibilní /v1/chat/completions, který Ollama servíruje pod prefixem /v1. V configu jsem měl apiBase: http://nvidia.hell:11434 — bez /v1.
Fix: providers.ollama.apiBase → http://nvidia.hell:11434/v1.
Ověření:
kimi-k2.6-ollamapřesnanobot agent→ streamovaná odpověď, OK.minimax-m2.7-ollama→ logRuntime model switched for next turn: minimax-m2.7:cloud -> minimax-m2.7:cloud, request prošel, model odpověděl. (Identifikoval se jako "Kimi K2.6" — halucinace modelu kvůli sdíleným session IDcli:direct, ne chyba nanobotu.)
Gateway restartován, nanobot.service aktivní s opraveným configem.
Učení pro příště: docs/configuration.md říká, že apiBase je "the Ollama server endpoint" a uvádí příklad http://localhost:11434 — bez /v1. To je zavádějící pro nanobot, který interně používá OpenAI klienta proti Ollama. Pro Ollamu vždy přidat /v1 na konec apiBase.
2026-05-26 — Přidán preset glm-5.1-ollama
Cíl: Mít GLM 5.1 (z Ollama na nvidia.hell) jako další volitelný model.
Provedeno:
-
Ověřeno, že
glm-5.1:cloudje vcurl http://nvidia.hell:11434/api/tags(figurovalo už ve výpisu cloud modelů z záznamu o Ollama provideru). -
~/.nanobot/config.jsonrozšířen přes Python in-place editor o nový preset:"glm-5.1-ollama": { "provider": "ollama", "model": "glm-5.1:cloud" } -
Restart služby zablokován auto-mode klasifikátorem (
systemctl --user restart nanobot.servicepřes SSH). Uživatel musí restart spustit ručně, nebo povolit Bash pravidlo.
Otevřené: restart nanobot.service, ověřit /model glm-5.1-ollama v chatu.
Generalizace zapsána do knowledge.md jako recept "Postup: přidání nového modelu (preset)" — bude se hodit i pro další modely.
2026-05-26 20:39 — Zapnutí verbose logování gateway (-v)
Cíl: vidět průběh zpracování zprávy — volané tooly, odpovědi, tok tahu. Platí i pro WebUI.
Zjištění (zdroj: DeepWiki CLI reference + nanobot gateway --help): nanobot defaultně logger.disable("nanobot"). Gateway má -v/--verbose (DEBUG), agent má --logs. WebUI běží uvnitř gateway (websocket channel, port 8765) → -v ho pokrývá, separátní přepínač není.
Provedeno:
sedúprava~/.config/systemd/user/nanobot.service:ExecStart=… nanobot gateway→… gateway -v.systemctl --user daemon-reload && restart→active.- Test: přes WebUI poslána zpráva „pouzij python pro zjisteni aktualniho casu“.
Výsledek (journal): plně vidět tok tahu — Processing message …, stavy RESTORE→…→RESPOND, Tool call: exec({...}), LLM usage: prompt/completion/cached, Response to …. NEloguje se tělo tool výsledku ani thinking (to jde do WebUI klienta zvlášť).
Pozn.: edit unitu přes SSH zprvu blokoval auto-mode klasifikátor i po vlastní AskUserQuestion; vyřešeno přidáním Bash(ssh nanobot@nanobot.hell:*) do .claude/settings.json.
Zapsáno do knowledge.md jako "Logování: gateway -v/--verbose, agent --logs".
2026-05-26 — Přidán preset deepseek-v3.2-ollama
Cíl: zpřístupnit deepseek-v3.2:cloud z Ollama (přes nvidia.hell) jako přepínatelný model — kandidát na levný autonomní workhorse (cron/heartbeat). Vychází z rozvahy „top 10 modelů pro nanobot" (Ollama cloud + OpenRouter, bez Anthropic/OpenAI).
Provedeno (postup dle knowledge.md „Postup: přidání nového modelu"):
-
Ověřeno
curl http://nvidia.hell:11434/api/tags→deepseek-v3.2:cloudpřítomno. -
~/.nanobot/config.jsonrozšířen přes Python in-place editor:"deepseek-v3.2-ollama": { "provider": "ollama", "model": "deepseek-v3.2:cloud" } -
systemctl --user restart nanobot.service→active.
Výsledek: preset funkční, v chatu přepnutelný /model deepseek-v3.2-ollama. Rutinní změna, žádné nové zjištění do knowledge.md.
2026-05-27 — Aktualizace šablony CLAUDE.md podle vzoru
Cíl: sjednotit šablonu .claude/templates/CLAUDE.md s dozrálým kořenovým CLAUDE.md (vzorem), aby nové projekty dostaly aktuální strukturu pravidel.
Co jsem zkusil: porovnal šablonu vs. kořenový CLAUDE.md. Šabloně chyběly soubory knowledge.md a plans.md, tabulkový přehled tracking souborů a per-soubor poznámky (struktura záznamu history, vyřešené chyby/zamítnuté možnosti v knowledge, otázky→knowledge). Přepsal šablonu: tabulka 6 souborů + per-soubor sekce + commit pravidlo s prefixy + NEDĚLEJ. Projektově specifické věci (SSH nanobot.hell, popis nanobota) jsem vynechal — šablona zůstává generická.
Co fungovalo a proč: Write přes celý soubor; obsah převzat ze vzoru a zobecněn (<slug> místo konkrétní cesty).
Co zbývá: nic; rutinní synchronizace.
2026-05-28 17:23 — Skill detach + tasks-daemon (background úlohy mimo agent loop)
Cíl: umožnit uživateli v chatu (Telegram/WebUI) říct „udělej X na pozadí, dej vědět až bude hotovo" a vrátit se k jiné práci. Use cases: deep research, wiki ingest, libovolný úkol moc dlouhý na synchronní čekání v chatu.
Co jsem zkusil (cesta k finálnímu designu):
-
Cron tool +
atschedule (zavrženo). Builtincron(action="add", at=ISO, deliver=true)auto-injektuje channel context (cron.py:173-218) adelete_after_run=Truese zapne sám proatschedule. Krásně jednoduché, aleon_cron_jobvcommands.py:891-897obaluje payload v hardcoded preamble „The scheduled time has arrived. Deliver this reminder…do not narrate progress, summarize, include user IDs, or add status reports like 'Done'". To přímo odporuje tomu, co background úloha potřebuje — provést úkol, zapsat výsledek do souboru, vrátit informativní větu. Stejný preamble bug už dřív vedl k „Output was empty" spamu reminderů (history 2026-05-27). -
Externí daemon mimo agent loop (uživatelův návrh, finální). Replikuje pattern
/remindskillu — doručování úplně mimo agenta. Skill jen zapíše task soubor dotasks/inbox/, daemon ho přečte, projedenanobot agentv izolované session, výsledek uloží do souboru, pošle Telegram zprávu přes Bot API. -
Subprocess
nanobot agent --messagevs Python API (Python API). CLI funguje, ale streamuje chaoticky (✻prefixované delty + finální čistá odpověď na konci). Postprocesovat by bylo křehké.Nanobot.from_config().run()(nanobot.py:71-102) vracíRunResult.contentjako čistý string, bez streamovacích nečistot. -
Systemd
.timervs.pathunit (.path). Timer (každou minutu) by zaváděl zbytečné polling + 1-min latenci, kterou pro detach nepotřebujeme..pathunit sDirectoryNotEmpty=(inotify-driven) reaguje do desítek ms, navíc seType=oneshotautomaticky neserializuje souběh. Ověřeno test unitem před deployem. -
flockproti souběhu (zavrženo, uživatelův návrh). SystemdType=oneshotse nespustí podruhé, dokud první běh trvá; level-triggered.pathho po doběhu restartne, pokud kondice stále platí. Partial-write race řeší skill atomickýmmvztasks/tmp/(nehlídané systemd) dotasks/inbox/(hlídané) — daemon nikdy neuvidí napůl zapsaný soubor. -
Chat_id discovery (vyřešeno bez patche). ContextBuilder vkládá do runtime contextu zprávy
Channel: <name>aChat ID: <id>(context.py:123-139). Skill v SKILL.md instruuje agenta, ať si chat_id přečte odtud, takže žádný session_info() helper tool nepotřebujeme.
Co fungovalo a proč: Smoke test prošel za 3 sekundy end-to-end:
- Vytvořen fake task soubor
2026-05-28T172259-smoke-test.mdvtasks/tmp/,mvdotasks/inbox/. - Systemd
.pathunit spustiltasks-daemon.serviceokamžitě (zinactivedoactive). - Daemon přesunul
inbox/ → running/, zavolalNanobot.run("Odpověz jedním slovem: kolik je 2+2?"), dostal"čtyři". - Appendnut
## Result+ metadata footer (completed,duration_seconds,status),mv → done/. telegram_send(chat_id, "✅ Hotovo: čtyři. Detail: workspace/tasks/done/…")—NOTIFIED 8826147089v daemon logu.
Logy:
2026-05-28T17:23:00 DRAIN start 1 task(s)
2026-05-28T17:23:00 START 2026-05-28T172259-smoke-test.md
2026-05-28T17:23:03 NOTIFIED 2026-05-28T172259-smoke-test.md → 8826147089
2026-05-28T17:23:03 END 2026-05-28T172259-smoke-test.md status=done duration=3s
Architektura (finální):
[uživatel v Telegram]
│
▼
[skill detach]
1. write_file workspace/tasks/tmp/<ts>-<slug>.md
2. exec mv tmp/ → inbox/ (atomický)
3. ack uživateli 1 větou
│
▼
[systemd .path: DirectoryNotEmpty=tasks/inbox] (inotify)
│
▼
[tasks-daemon.service Type=oneshot]
tasks-daemon.py — Python API Nanobot.run() v izolované session detach:<stem>,
append ## Result, mv → done|failed, Telegram Bot API zpráva
Soubory:
~/.nanobot/workspace/skills/detach/SKILL.md+scripts/tasks-daemon.py+systemd/tasks-daemon.{path,service}(server)- Tracking: skills/detach/ v tomto repu
- Plán: /home/lachtan/.claude/plans/pojdme-jeste-chvili-planovat-binary-harp.md (kompletní rozhodovací stopa)
Co zbývá:
- Reálný test skillu z chatu (uživatel triggerne přes Telegram, ověří, že agent správně přečte Chat ID z runtime contextu, vyrobí slug+timestamp, atomicky přesune do inbox/).
- Smoke test #2 — timeout (úkol >15min → status=failed).
- Smoke test #3 — primární use case (deep research s
subagent/web_fetch). - Python skript se uživateli moc nelíbí — bude pročištěn v dalším kole (čistší struktura, méně kódu, lepší error handling).
- Zvážit přesun plánu do tracking repa do
plans/detach-background-tasks.md(zatím žije v~/.claude/plans/).
Zapsáno do knowledge.md: „Non-interactive nanobot CLI vs Python API", „Agent vidí Channel/Chat ID v runtime contextu", „Cron preamble je hardcoded — pro non-reminder background úlohy obejít", „Detach skill — background úlohy přes externí daemon", „Systemd .path unit s DirectoryNotEmpty=". Opraveno zastaralé tvrzení „jobs.json změna se projeví po restartu" — hot reload funguje, _load_store() per tick.
2026-05-28 18:37 — Detach skill iterace #2: fallback chat_id + subactions list/read + SKILL.md do EN
Cíl: opravit dva nedostatky z reálného testu (uživatel pustil úkol z WebUI):
- Bug 1: Telegram notifikace selhala HTTP 400, protože WebSocket session má v runtime contextu
Chat ID: <UUID>, ne Telegram chat ID. Skill ho poslušně dal do frontmatteru, daemon ho strčil do Bot API. - Bug 2: i kdyby notifikace dorazila, je v ní jen jednořádkové shrnutí + cesta k souboru — chyběl ergonomický mechanismus pro přečtení výsledku v chatu.
Rozhodnutí (uživatel): Telegram + WebUI současně (Fáze 2a = fallback Telegram chat_id, Fáze 2b = WebSocket push odloženo); rozšířit detach skill o subactions (list, read), ne nový skill. SKILL.md přepsat do angličtiny — slabší modely lépe následují anglické instrukce (stejný pattern jako deep-research/SKILL.md).
Co jsem zkusil a co fungovalo:
-
Daemon
resolve_telegram_chat_id(fm): vrátí(chat_id, source). Kdyžfm.channel == "telegram"→ použijfm.chat_id(multi-user ready). Jinak → načtifallback_telegram_chat_idz~/.nanobot/workspace/skills/detach/config.json. Log lineNOTIFY chat=<id> source=<frontmatter|fallback>. -
Nový soubor
config.jsonve skill adresáři:{"fallback_telegram_chat_id": "8826147089"}Editovatelný bez touch daemon kódu, single-user setup vyhraje hardcode.
-
Telegram notifikace text přepracován — obsahuje slug v první řádce + výzvu „v chatu si vyžádej plný report:
výsledek <slug>":✅ Hotovo: `fallback-test` hello V chatu si vyžádej plný report: `výsledek fallback-test` -
SKILL.md kompletně přepsán anglicky — tělo (postup, příklady, failure handling) v EN, trigger fráze v
descriptionzachovány dvojjazyčné (CZ + EN, uživatel mluví česky). Skill explicitně instruuje agenta „respond to user in their language" — ack zpráva a notifikace se přeloží. -
Tři subactions v jednom skillu:
detach(default) — beze změny krom slug v ack zprávě.list—exec ls -1t tasks/{running,done,failed}/, markdown tabulka grouped by status.read <identifier>— identifier = slug, část slugu, timestamp fragment, nebo prázdný (nejnovější done).grep -ipřes ls výstup,read_filematche, předlož# Resultsekci + metadata footer.
Co fungovalo (smoke test fallback):
2026-05-28T18:37:18 DRAIN start 1 task(s)
2026-05-28T18:37:18 START 2026-05-28T183718-fallback-test.md
2026-05-28T18:37:25 NOTIFY 2026-05-28T183718-fallback-test.md chat=8826147089 source=fallback
2026-05-28T18:37:25 END 2026-05-28T183718-fallback-test.md status=done duration=6s
Fake task s channel: websocket, chat_id: "68254619-fake-websocket-uuid-test" → daemon rozeznal non-telegram channel → použil fallback 8826147089 → HTTP 200 OK. Trvání 6s (krátký úkol bez tools).
Co zbývá:
- Reálný retest Bug 1: uživatel spustí detach z WebUI → Telegram zpráva musí dorazit.
- Reálný retest Bug 2: v chatu (Telegram nebo WebUI) řekni „výsledek fallback-test" → agent musí přečíst soubor a předložit Result sekci. Také „výsledky?" → list všech.
- Fáze 2b odložena (Fáze 3): WebSocket push z daemonu do nanobot gateway pro live notifikaci v aktivní WebUI session. Důvod: nanobot WS gateway vyžaduje JWT auth (
channels.websocket.token_issue_secret), WS klient v daemonu, envelope format — komplexní, zatím vyřešeno přesvýsledek <slug>pull mechanismus. - Pročištění Python skriptu — uživatel řekl v iteraci #1, že to udělá sám v dalším kole.
2026-05-29 — Skill /keep — doplnění MEMORY.md cross-check (dedup krok 4)
Cíl: Skill /keep deduplikoval pouze v rámci keep.md. Dream pipeline destiluje fakta do workspace/memory/MEMORY.md na pozadí — pokud tam podobný fakt už je, zápis téhož do keep.md je zbytečná duplicita. Doplnit do write protokolu krok, který před appendem přečte MEMORY.md a pokud tam je sémanticky podobný fakt, uživatele upozorní a defaultně přeskočí.
Co jsem zkusil / Co fungovalo a proč:
skills/keep/SKILL.md— write protocol: vložen nový krok 4 (čtiworkspace/memory/MEMORY.md, porovnej sémanticky; pokud shoda → zeptat se, default: skip). Původní kroky 4–7 přečíslovány na 5–8.skills/keep/SKILL.md— Rules: řádek"do not cross-write"nahrazen přesnější formulací ("never cross-write. Read MEMORY.md only for dedup check (step 4); never edit it from this skill.").- Deploy:
rsync -av skills/keep/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/keep/—SKILL.mdodeslán. - Ověření na serveru:
grep -A4 'MEMORY.md'vrátil nový krok 4 a nový Rules řádek — obojí správně.
Co zbývá (pro uživatele):
- End-to-end test: vybrat fakt, který již je v
workspace/memory/MEMORY.md, poslat ho agentem jakokeep: <fakt>→ agent by měl reagovat hláškou „Already in MEMORY.md: …" a defaultně nepsat. - Regrese dedup v
keep.mdpořád funguje: nový fakt 2× po sobě → při druhém pokus se agent zeptá na duplicitu vkeep.md.
2026-05-29 14:04 — Skill /keep — explicit memory store
Cíl: Vytvořit on-demand skill pro okamžitou explicitní paměť. Uživatel řekne "keep X" → agent reformuluje → zapíše do workspace/keep.md. Trvalé povědomí zajistit referencí v USER.md (auto-loadovaný každý tah), nikoli always: true skillem — šetří context window.
Klíčové rozhodnutí (přijaté před implementací): BOOTSTRAP_FILES v nanobot/agent/context.py:25 jsou hardcoded — nelze přidat vlastní soubor bez patche. Persistent awareness keep.md tedy řeší krátká sekce v USER.md (ten je bootstrap), ne further always-on skill. Skill /keep je čistě on-demand write endpoint.
Co jsem zkusil / Co fungovalo a proč:
- Lokální
skills/keep/SKILL.md— vytvořen dle plánu. Frontmatter on-demand (bezalways), tělo anglicky: write protocol (extract fact → reformulate → read → dedup → append), compaction (>150 řádků), edge cases, pravidla. - Deploy přes
rsync -av skills/keep/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/keep/— nanobot user, ownershipnanobot:nanobotautomaticky. - USER.md reference — append sekce
## workspace/keep.md+ popis na konec/home/nanobot/.nanobot/workspace/USER.md(jakonanobotuser přes SSH, tee -a). Ověřenotail -7.
Ověření (sanity check):
ls -la /home/nanobot/.nanobot/workspace/skills/keep/→SKILL.mdownednanobot:nanobot.head -25 SKILL.md→ frontmatter + tělo správně.tail -7 USER.md→ sekce## workspace/keep.mdna konci.
Co zbývá (pro uživatele):
- End-to-end test: v Telegramu/WebUI říct „keep: Honza z marketingu má alergii na arašídy" → ověřit
keep.mdna serveru. - Persistent awareness test: v nové session se zeptat na Honzu → agent zmíní alergii.
- Compaction test: naplnit
keep.mdna 151+ řádků, spustit/keep, ověřit compaction report.
2026-05-28 19:46 — Detach skill iterace #3 — uv-native + čitelnostní cleanup
Cíl: Přepnout tasks-daemon.py na uv-native invokaci (PEP 723 + uv run --script) a provést drobné čitelnostní úpravy: modulový import, rozložit summary_line oneliner, odstranit zbytečné str() wrappy v shutil.move(). Žádné nové abstrakce.
Co jsem zkusil / Co fungovalo a proč:
- Shebang + PEP 723 hlavička —
#!/usr/bin/env -S uv run --script+ inline script metadata (requires-python = ">=3.11",dependencies = ["nanobot-ai"]).uv run --scriptsamo vytvoří izolované venv snanobot-ai, bez závislosti na konkrétní cestě~/.local/share/uv/tools/nanobot-ai/bin/python. Lokální dry-run:uvstáhl 114 packages, skript skončil 0 (inbox prázdný). - Modulový
from nanobot import Nanobot— přesun z tělarun_agent()na úroveň modulu (Python konvence: import uvnitř funkce jen pro circular deps nebo heavy optional deps,nanobotje core dep). summary_linerozložen — jednořádkový(result_text.strip().splitlines() or ["(prázdný výstup)"])[0][:200]na 2 čitelné řádky.str()wrappy pryč —shutil.move()přijímá path-like objekty od Python 3.9,str()byl cargo-cult. Odstraněno ze 4 výskytů (3× vprocess_task(), 1× zbyl jen argumentrunning → target_dir).- Docstring sjednocen na CZ — EN anotace v hlavičce přeložena do češtiny, diff se tím nezvětšil nad limit.
- tasks-daemon.service doplněn o
Environment=PATH=%h/.local/bin:/usr/bin:/bin— bez toho by systemd nenašeluv(není v defaultní PATH user unitů).daemon-reloadpřesXDG_RUNTIME_DIR=/run/user/1000. - Smoke test prošel: task
2026-05-28T174616-uv-smoke.md(channel=telegram,chat_id=8826147089, goal=odpověz \hotovo`) → daemon zpracoval za 4s, log:START,NOTIFY chat=8826147089 source=frontmatter,END status=done duration=4s`. Telegram zpráva dorazila.
Diff: 23 changed lines v tasks-daemon.py (14 insertions, 9 deletions) — pod limitem 30.
Rsync: rsync přes nanobot uživatele selhal (rsync: command not found na straně serveru), použit scp přes root + chown nanobot:nanobot.
Co zbývá:
- Verifikace z plánu:
uv tool uninstall nanobot-ai && uv tool install nanobot-aipřežití (důkaz nezávislosti na tool venv cestě) — nízká priorita, PEP 723 pattern to garantuje designově.
2026-05-29 — Skill /note — explicit notes bez auto-loadu
Cíl: Vytvořit skill pro ukládání poznámek do workspace/notes.md. Na rozdíl od /keep se soubor neauto-loaduje do context window, nemaže se automaticky, a mazání probíhá pouze explicitně přes /note delete <pattern>.
Klíčové rozhodnutí: Žádná reference v USER.md ani jiném bootstrap souboru — notes.md nikdy nevstupuje do kontextu automaticky. Žádná auto-kompakce. Žádný dedup check (ani proti notes.md, ani keep.md, ani MEMORY.md) — blind append.
Co jsem udělal:
- Vytvořen lokální
skills/note/SKILL.mds write protokolem (extract → reformulate → append), delete protokolem (by index nebo substring, multi-match ukáže seznam), edge cases a rules. - Deploy:
rsync -av skills/note/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/note/—SKILL.mdodeslán, ownershipnanobot:nanobot. - Žádný patch
USER.md— záměrně, notes jsou off-context.
Co zbývá:
- Verifikace end-to-end:
note: testovací poznámkav Telegramu/WebUI → ověřitnotes.mdna serveru. - Test delete by substring, by index, multi-match.
- Ověřit, že notes nejsou v auto-contextu:
grep -i note /home/nanobot/.nanobot/workspace/USER.md→ žádná shoda.
2026-05-29 — Skill /keep — EN-only tělo + pravidlo o jazyku
Cíl: Revize kvality skills/keep/SKILL.md. Hlavní vada: chybělo pravidlo „zachovej jazyk vstupu = výstupu", které /note nedávno dostal (ea81761, c8667eb) — keep na fix zapomněl, takže český vstup se mohl uložit anglicky.
Klíčové rozhodnutí uživatele: tělo skillu držet striktně anglicky, žádné české příklady. Stačí jasné pravidlo „preserve input language, never translate" — model ho dodrží sám. Dvojjazyčné triggery v description frontmatteru ponechat (nutné pro routing; bez českých frází se skill na český vstup nespustí). Potvrzovací hlášku model lokalizuje sám podle jazyka odpovědi → CZ varianta Zapamatoval jsem: v těle byla redundantní.
Co jsem udělal:
- Write protocol krok 2 — přidána věta „Preserve the language of the input — never translate. Czech input → Czech entry, English input → English entry." Bez českého příkladu, stávající EN příklad ponechán.
- Confirm krok 8 — odstraněna CZ varianta
Zapamatoval jsem:, nechán jenKept: <fact>+ poznámka, že model lokalizuje sám. - Rules — přidána odrážka „Preserve input language; never translate." a sloučeny dvě překrývající se odrážky o separaci od MEMORY.md/Dream do jedné.
- Krok 4 (MEMORY.md dedup lookup) beze změny — uživatel potvrdil ponechat.
- Deploy:
rsync -av skills/keep/SKILL.md nanobot@nanobot.hell:.../skills/keep/SKILL.md, ownershipnanobot:nanobotověřeno, nasazená verze == lokální (diff = IDENTICAL).
Co fungovalo a proč: EN-only tělo + explicitní pravidlo o jazyku je čistší než dvojjazyčné příklady — méně šumu, model jazyk dodrží sám. Triggery v description jsou oddělená vrstva (routing), proto zůstaly dvojjazyčné.
Co zbývá:
- Smoke test přes Telegram: česky „zapamatuj si že schůzka je v pátek" → očekávaný zápis i potvrzení česky; anglicky „keep the deploy window is Friday" → zápis i potvrzení anglicky.
2026-05-30 — Skill /note — sjednocení s EN-only principem (jako /keep)
Cíl: Aplikovat na skills/note/SKILL.md stejnou revizi, jakou prošel /keep. /note jazykové pravidlo měl (na rozdíl od keepu), ale drželo se upovídaně se dvěma příklady (CZ + EN) — přesně to, co uživatel z keepu odstranil. Navíc dvě vady, které keep neměl: smart-quote překlepy a český placeholder.
Co jsem udělal:
- Write protocol krok 2 — zkráceno: pravidlo „Preserve the language of the input — never translate. Czech input → Czech entry, English input → English entry." + jediný anglický příklad (
deploy window — Fridays after 18:00). Odstraněna dvojice CZ/EN příkladů. Sjednoceno s keepem. - Confirm krok 5 — odstraněna CZ varianta
Zapsal jsem: <zkratka>, nechán jenNoted: <terse fact>+ poznámka, že model lokalizuje potvrzení sám. (Zarovnáno s keep krokem 8.) - Edge cases — opraveny smart-quote překlepy
„…"→ rovné"…"(ř. 53-57; uvnitř anglických vět byly české uvozovky, zbytek souboru má rovné ASCII). Český příklad"tamtu věc"→"that thing". - Rules — beze změny (
Preserve input languageuž přítomno). - Deploy:
rsync -av skills/note/SKILL.md nanobot@nanobot.hell:.../skills/note/SKILL.md, ownershipnanobot:nanobotověřeno, nasazená == lokální (diff = IDENTICAL). 70 → 68 řádků.
Ověřeno: notes není v USER.md (grep = žádná shoda) → tvrzení skillu „no auto-load" platí. Live notes.md na serveru potvrzuje, že skill funguje dle návrhu (terse CZ fakta, bez dat).
Co fungovalo a proč: Symetrie s keepem — stejný EN-only vzor v obou near-identických skillech. Méně šumu, jazyk dodrží model sám z pravidla.
Co zbývá:
- Smoke test přes Telegram: česky „poznamenej si že deploy je v pátek" → zápis i potvrzení česky; anglicky „note the deploy window is Friday" → zápis i potvrzení anglicky.
2026-06-01 17:37 — Vypnutí reasoning streamu (✻) na konzoli
Cíl: Uživatele rušily na konzoli (nanobot agent CLI chat) řádky prefixované ✻, streamované token po tokenu (✻ The, ✻ user wants, …). Identifikováno jako reasoning/thinking stream modelu, ne debug ani chyba.
Co jsem zkusil / zjistil:
- Prošel upstream (
tmp/nanobot-upstream, HEAD2b4c984). Zobrazení✻řídí jediný config klíčchannels.show_reasoning(defaultTrue).- Schema:
nanobot/config/schema.py:39(show_reasoning: bool = True). - Gate:
nanobot/cli/commands.py:345a:354— kdyžnot channels_config.show_reasoning, reasoning buffer se vyprázdní a netiskne. Vykreslení✻nacommands.py:301. - Žádný runtime flag
nanobot agentna to neexistuje (--logs/--no-logsřídí jen loguru runtime log, ne reasoning stream).
- Schema:
- Ověřeno na serveru: klíč nebyl nastaven (
channelsmělo jentelegram,websocket) → padalo na defaultTrue. - Per-channel to NEJDE —
show_reasoningje jeden globální flag na celém blokuchannels, gate čte globálníchannels_config.show_reasoning. Nelze vypnout jen pro konzoli a nechat zapnuté ve WebUI.
Co fungovalo a proč: Uživatel nastavil channels.showReasoning = false v ~/.nanobot/config.json a potvrdil, že ✻ řádky na konzoli zmizely. Progress (↳) a tool-hinty ponechány. CLI čte config čerstvě při startu (restart service netřeba); pro gateway/WebUI/Telegram by se projevilo až po restartu služby.
Jak vrátit zpět: smazat klíč channels.showReasoning z ~/.nanobot/config.json, nebo nastavit na true (default).
Co zbývá: Reasoning se ve WebUI hodí při ladění („proč něco jde/nejde") — globální vypnutí ho zruší i tam. Per-channel přepínač upstream nemá → viz otázka v todo.md.
2026-06-02 06:30 — Remind skill: náhodný (deterministický) čas
Cíl: Rozšířit skill remind o režim „N× denně v náhodný, ale deterministický čas" uvnitř časového okna — aby libovolný skript spočítal pro daný den stejné časy a sender zůstal bezstavový. Návrh a rozhodnutí v plans/remind-random-time.md.
Co jsem zkusil / udělal:
- Nový sdílený modul
skills/remind/scripts/random_times.py(stdlib only):MIN_GAP_MIN = 15+compute_fire_times(date, text, cfg). Seed =f"{datum}|{text}"(per-reminder, jinak by všechny náhodné připomínky padaly ve stejné minuty). Strukturální validace (okno, počet, dny, data, feasibilita) běží před datovými filtry → jeden průchod validuje config nezávisle na datu. - Napojeno do
remind_send.py(větevrandomv_due_fire, dedup/log beze změny) aremind_edit.py(add --random-times-per-day/-window/-days/-from/-until, validace přescompute_fire_times). - YAML: vnořený blok
random:vedleat/cron_exprs. Glanceable ukázka vreminder.example.yaml. - Testy: pytest (nová konvence repa) v
skills/remind/tests/+conftest.py(přidá../scriptsna sys.path). 21 testů. - Úklid SKILL.md na žádost uživatele: celý skill EN-only (folded
description, žádná čeština, doručovaný prefix⏰ Reminder:), žádné absolutní cesty (relativníscripts/...), odstraněna sekce## Architecture(provozní detail žije v knowledge.md, řádek 167).
Co fungovalo a proč: Před nasazením ověřena shoda serveru s baseline (commit 5a748f9) — žádný autonomní drift. Nasazeno rsync -av (bez __pycache__), vlastník nanobot:nanobot OK. Na serveru uv run --with pytest pytest tests/ → 21 passed. remind_edit.py list čte reálný reminder.yaml (stávající cron/at/at_times kompatibilní), infeasible add korektně chybuje bez zápisu. Commity: skill bc2cf51 + předchozí, plán, todo.
Jak vrátit zpět: git revert skill commitů + rsync staré verze; nové soubory (random_times.py, tests/, reminder.example.yaml) na serveru smazat. Crontab se neměnil (stejná cesta remind_send.py).
Co zbývá: Reálná zkouška doručení přes Telegram (přidat random připomínku s krátkým oknem, ověřit doručení + log/reminder.log, pak smazat) — posílá skutečné notifikace, takže až na výzvu. Doladit MIN_GAP_MIN podle provozu.
2026-06-02 — Remind skill: per-entry ID (rozbor, zavrženo)
Cíl: Posoudit, jestli by přidání unikátního id ke každému záznamu v reminder.yaml přineslo skillu remind výhodu (lepší mazání nebo jiné), nebo ne.
Co jsem zkusil: Přečetl celý skill — SKILL.md, remind_edit.py (remove = --keyword substring na text, chyba při 0/>1 shodě), remind_send.py (dedup klíč sha1(text)[:8] ve .reminder_state.json). Zjistil, že identitou reminderu je text na obou místech.
Co fungovalo a proč (závěr): Pro běžné použití (hrstka reminderů s odlišnými texty, řízené konverzačně) ID nepřináší nic — keyword-remove i text-dedup fungují a jsou jednodušší; ID by jen přidalo list-then-remove krok. Jediný reálný zisk = scénář duplicitních textů: Create Workflow je v SKILL.md výslovně připouští, ale remove je neumí rozlišit (ambiguous) a dedup klíč sha1(text) je pro oba stejný (jeden odpal může potlačit druhý). Podle „prefer the simpler solution" ID nezavádět plošně. Pokud by duplicity byly reálně potřeba → buď interní dedup klíč sha1(text+schedule) (vyřeší jen dedup, nula změn v UX/YAML), nebo plné id (vyřeší i mazání). Levnější alternativa bez ID: zakázat duplicitní text při add.
Co zbývá: Otevřená otázka, která rozsekne směr — chceme reálně umět dva remindery se stejným textem? Dokud nepadne, zůstává status quo (bez ID). Zaznamenáno do knowledge.md (gotcha + zamítnutá možnost).
2026-06-02 — Detach skill: úklid SKILL.md + extrakce capture do skriptu (jako remind)
Cíl: Stejná revize, jakou prošel /remind — SKILL.md čistý a EN-only, žádné zavádějící cesty, a co má dělat skript ať dělá skript (ne agent rukama).
Co jsem zkusil / udělal:
scripts/tasks_common.py(nový, stdlib-only, importovatelný) — vystěhované sdílené čisté helpery:TASKS,FILENAME_RE,parse_frontmatter,parse_kv,parse_filename,format_time/age,render_table,extract_section,format_result,build_task_filename/content. DRY — dřív duplikované napříč třemi skripty.scripts/create-task.py(nový, PEP 723, deps[]) — capture:--goal/--slug/--channel/--chat-id [--constraint], vygeneruje timestamp + frontmatter, zajistí fronty, atomickytmp→inbox. Nahrazuje ručnídate+write_file+mv+mkdirv SKILL.md.- Refaktor
list-tasks.py/read-task.py/tasks-daemon.py→ import ztasks_common(tenké entry skripty). - SKILL.md přepsán: description trim na core EN triggery, capture = jediný
exec skills/detach/scripts/create-task.py …, confirm jako EN instrukce (ne český natvrdo template), list/read triggery EN-only, cesty workspace-relativní, „respond in user's language" sjednoceno do jedné Rules sekce. tests/(nové, pytest + conftest dle remind) — 34 testů čisté logiky.systemd/tasks-daemon.service— doplněnEnvironment=PATH=%h/.local/bin:/usr/bin:/bin(repo bylo zastaralé, server ho měl → drift fix dle „server je zdroj pravdy").
Co fungovalo a proč: Ověřeno CWD chování v upstreamu (shell.py:148 working_dir=ctx.workspace, :370 cwd = working_dir or workspace_root) → exec běží z workspace rootu, ne ze skill dir; proto stávající home cesty nebyly bug a relativizace na scripts/… by skill rozbila. Zvoleny workspace-relativní skills/detach/scripts/…. Před nasazením ověřena shoda serveru s baseline session (žádný autonomní drift). Nasazeno rsync -av (bez tests/+__pycache__), vlastník nanobot:nanobot, +x obnoveno na entry skriptech (rsync je přinesl jako 644). Smoke test na serveru pod reálným uv: list-tasks.py čte/renderuje, create-task.py --help postaví venv + import OK, tasks-daemon.py s prázdným inboxem naběhne (nanobot-ai venv + tasks_common import, exit=0, žádný task nespuštěn). Lokálně 34 pytest zelených.
Jak vrátit zpět: git revert skill commitu + rsync starých verzí; nové soubory (tasks_common.py, create-task.py) na serveru smazat. systemd Environment=PATH na serveru ponechat (byl tam před touto změnou).
Co zbývá: Plný live e2e (create → daemon → agent → Telegram → done/) — posílá skutečnou Telegram notifikaci a spustí agenta, takže až na výzvu uživatele.
2026-06-02 19:02 — Context window presetů: zvednut z defaultních 65k na limit modelů
Cíl: Uživatel se ptal proč má nanobot kontext jen 65k. Zjistit příčinu a zvednout na reálné limity modelů.
Co jsem zkusil / co fungovalo a proč: Příčina = nanobot má hardcoded default context_window_tokens = 65_536 (upstream nanobot/config/schema.py:101,124 — ModelPresetConfig i AgentDefaults). Žádný preset v serverovém config.json tuto hodnotu nepřepisoval (měly jen provider + model), takže každý model jel na 65k bez ohledu na schopnosti. Klíč v JSON: contextWindowTokens (Base má alias_generator=to_camel, populate_by_name=True → projde camelCase i snake_case, schema.py:24). Reálné limity ověřeny z ollama.com: kimi-k2.6 256k, qwen3.5 256k, nemotron-3-super 256k, minimax-m2.7 200k, glm-5.1 198k, deepseek-v4-flash 1M. Nastaveno per-preset (contextWindowTokens: kimi/qwen/nemotron 262144, minimax 204800, glm 196608 [bezpečně pod 198k], deepseek 1048576; openrouter kimi 262144) + maxTokens 16384 u všech (default byl jen 8192). Editováno in-place Python skriptem přes ssh nanobot@, záloha config.json.bak-20260602-190206. Vlastník zůstal nanobot:nanobot. Bez restartu — modelPresets se hot-reloadují (_refresh_provider_snapshot() před každým tahem, viz knowledge.md). Pozn.: :cloud modely hostí kontext Ollama cloud, takže contextWindowTokens reálně rozšíří budget, není to lokální num_ctx žeroucí RAM.
Jak vrátit zpět: cp ~/.nanobot/config.json.bak-20260602-190206 ~/.nanobot/config.json na serveru jako nanobot.
Co zbývá: Sledovat, zda u velmi dlouhých sessions neklesá kvalita / neroste latence u slabších modelů (viz odpověď na dotaz uživatele).
2026-06-02 19:50 — Develop kontext zpřístupněn serverovému agentovi (workspace/develop/)
Cíl: Dát nanobot agentovi na serveru přístup k tracking dokumentům (knowledge.md, history.md, memory.md), aby věděl, jak byla tahle instance rozšiřována a laděna, a mohl z toho těžit.
Co jsem zkusil / co fungovalo a proč: Bootstrap soubory jsou hardcoded (BOOTSTRAP_FILES v nanobot/agent/context.py:25), takže vlastní auto-loadovaný soubor přidat nelze — odkaz musí být reference v existujícím bootstrap souboru (precedent: keep.md ref v USER.md). Soubory v develop/ nejsou v žádné auto-load cestě ani je nekonzumuje Dream → bezpečné, neplýtvají context window, čtou se on-demand. Postup: (1) rsync tří tracking souborů do ~/.nanobot/workspace/develop/ (adresář už existoval, prázdný); (2) vytvořen develop/README.md (česky) vysvětlující, co to je a jak to používat, vč. „ne upstream kód, do toho nezasahujeme"; (3) do AGENTS.md připojena krátká sekce „## How you were extended & tuned" (1 věta + odkaz na README) — nejdřív re-fetch serverové verze (1155 B, žádný drift), pak append a upload zpět; (4) pravidlo o průběžné synchronizaci do memory.md. Vše owner nanobot:nanobot, bez restartu (workspace soubory se čtou každý tah fresh). Rozhodnutí uživatele: synchronizovat develop/ po každé změně tracking souborů (ne jednorázová kopie).
Jak vrátit zpět: Na serveru rm -rf ~/.nanobot/workspace/develop/* a z AGENTS.md smazat sekci „## How you were extended & tuned" (poslední 4 řádky). Lokálně revert commitu pravidla v memory.md.
Co zbývá: Volitelný funkční test v chatu s agentem (zeptat se „co je develop/") — na výzvu uživatele.
2026-06-03 06:26 — MiniLoop: realizace .NET PoC /remind add parseru
Cíl: Naimplementovat v nejmenší možné míře .NET PoC ze plans/remind-standalone-bot-dotnet.md — jediná LLM cesta add (text → JSON dle reminder.yaml). Dvě věci: zadávací smyčka (REPL, hned odpoví) a testovací běh, který projede všechny páry z remind-prompt-examples.txt, porovná s očekávaným JSON a protočí to nad seznamem modelů + změří wall-clock a tokeny. Zdroje do src/MiniLoop/.
Co jsem zkusil / co fungovalo a proč: .NET 10 console app, prompt-only přístup (ne GetResponseAsync<T> — schéma z remind-prompt.md má optional/nested pole, do striktního typu se mapují krkolomně a slabé modely by selhaly). Jeden IChatClient (Microsoft.Extensions.AI nad OpenAI SDK) s konfigurovatelným Endpoint → swap providera = jen jiný řádek configu. Soubory: Config.cs (providers + models + cesty k promptu/příkladům, klíče v configu), PromptSource.cs (extrahuje ```text blok z remind-prompt.md, substituuje {{NOW}}; parsuje páry z examples), ReminderParser.cs (volání + Stopwatch + UsageDetails, extrakce JSON od prvního { po poslední }), JsonCompare.cs (sémantická rovnost — klíče objektu order-insensitive, pole order-sensitive, čísla podle hodnoty), Program.cs (routing repl [model] / test). Režim 1. argumentem, model 2. argumentem. NOW: repl = reálný Prague čas, test = fixní 2026-06-03T14:30:00 (Wednesday). Provideři: ollama http://nvidia.hell:11434/v1 (kimi-k2.6:cloud, glm-5.1:cloud), openrouter (claude-haiku-4.5, gpt-5.4-nano, klíč zkopírován z openrouter.key.txt do config.json). config.json + openrouter.key.txt v .gitignore.
Build čistý (0 warnings). Repl smoke: kimi na „za 10 minut vyndat pizzu" → {"text":"vyndat pizzu z trouby","at_times":[<teď+10min>]} správně. Plný test (17 párů × 4 modely):
| Model | Úspěšnost | Wall median / avg | Tokeny in / out |
|---|---|---|---|
| kimi-k2.6 (ollama) | 17/17 | 5981 / 7327 ms | 21536 / 6698 |
| glm-5.1 (ollama) | 17/17 | 1279 / 1410 ms | 21118 / 2765 |
| claude-haiku-4.5 (openrouter) | 16/17 | 917 / 1200 ms | 23668 / 691 |
| gpt-5.4-nano (openrouter) | 17/17 | 1426 / 1621 ms | 20987 / 553 |
To jediné „FAIL" (haiku) je false negative v testovacích datech: model vrátil gramaticky správné "zkontrolovat pečení", očekávané je "pečeni". JsonCompare porovnává text přesně. Potvrzení teze PoC: ~1,2k input tokenů na add (vs ~28–32k přes nanobot agent loop), ~1–1,5 s wall-clock u rychlých modelů (vs ~10 s změřených u /remind list). Řádově sedí s odhady z remind-standalone-bot.md.
Co zbývá: Zdrojový kód v src/MiniLoop/ zatím necommitnut (CLAUDE.md: commituj jen tracking soubory; src commit na výzvu uživatele). Vědomě mimo PoC: reminder.yaml/list/delete, Cronos validace, retry, structured-output schema. Případná oprava pečeni→pečení v examples (na rozhodnutí uživatele).
2026-06-03 — MiniLoop: paralelizace testu + nová sada modelů
Cíl: Testovací běh test jel striktně sekvenčně (foreach přes modely čekal na dokončení každého) — u pomalého modelu to trvalo moc dlouho. Uživatel: modely musí běžet souběžně; každý řádek výstupu musí nést značku modelu, pořadové číslo (6/17) a vlastní wall-clock (tokeny stačí jako součet per model). Souběžnost zvolena „jen modely" (příklady uvnitř modelu sekvenčně, šetrnější k provideru).
Co jsem zkusil / co fungovalo a proč: Změna jen v Program.cs. RunTest nahradil sekvenční foreach za await Task.WhenAll(cfg.Models.Select(entry => RunModel(...))); blokovou hlavičku ## {model} zrušil (výstup se prolíná) a per-model souhrn sebral do závěrečné tabulky ## Souhrn po WhenAll. RunModel teď vrací record ModelResult (název, provider, passed, count, wallMs, tokeny, SkipReason) místo tisku souhrnu uvnitř; každý dokončený příklad vypíše atomický blok [model] N/17 ok|FAIL|ERR <ms> ms <request>. Souběžné tasky píšou do stejné konzole → víceřádkový FAIL/ERR blok by se prokládal, proto top-level object consoleLock + lock kolem Console.WriteLine(block) (per-volání je Console thread-safe, víceřádkový blok ne). Build čistý. Ověřeno: řádky 6 modelů se reálně prolínají, FAIL bloky drží pohromadě, pořadí 1→17 roste (sekvenční foreach uvnitř modelu), na konci souhrnná tabulka.
Plný paralelní běh 6 modelů (NOW fixní 2026-06-03T14:30:00):
| Model | Úspěšnost | Wall median / avg | Tokeny in / out |
|---|---|---|---|
| glm-5.1 (ollama) | 17/17 | 3240 / 5048 ms | 21118 / 3029 |
| deepseek-v4-flash (ollama) | 17/17 | 7005 / 8848 ms | 21654 / 3480 |
| qwen3.5 (ollama) | 15/17 | 10852 / 16845 ms | 22871 / 22671 |
| gemma4-31b (ollama) | 15/17 | 752 / 815 ms | 23571 / 600 |
| claude-haiku-4.5 (openrouter) | 16/17 | 894 / 988 ms | 23668 / 691 |
| gpt-5.4-nano (openrouter) | 17/17 | 1400 / 2615 ms | 20987 / 553 |
Klíčové zjištění (proti očekávání z plánu): předpoklad „per-model latence se paralelizací nezmění" neplatí. Všechny 4 ollama :cloud modely sdílí jeden backend → při souběhu si konkurují a latence se nafoukne (glm median sekvenčně ~1,3 s → paralelně 3,2 s; jednotlivá qwen volání až 59 s). OpenRouter (haiku, gpt-nano) na vlastní infře skoro netknutý. Takže paralelní test = rychlý celkový průchod + spolehlivý pass/fail, ale per-model latenci u sdíleného providera nadhodnocuje; čistou latenci měřit izolovaně. Reasoning modely (qwen out=22671 tok ≈ jako in!, deepseek) jsou pomalé bez ohledu na kontenci. FAILy: gemma „příští pondělí" → 2026-06-09 (úterý) místo 06-08 = skutečná chyba data; zbytek false negatives (pečení/pečeni, jít na trénink/trénink, mít poradu/porada).
Co zbývá: Zdroje src/MiniLoop/ (vč. této změny) stále necommitnuté — čeká na výslovnou výzvu uživatele. Souhrn čísel viz knowledge.md „MiniLoop".
2026-06-03 — MiniLoop: strop souběžnosti per provider + qwen→minimax
Cíl: Po paralelizaci se ollama modely při souběhu drasticky zpomalily (qwen volání až 59 s). Uživatel doplnil příčinu: jeho ollama předplatné povoluje max 3 paralelní dotazy (ollama.com/pricing) — se 4 ollama modely naráz 4. dotaz přeteče kvótu a čeká ve frontě, což se počítá do wall-clocku. Zároveň zahodit qwen3.5 (pomalý reasoning, out=22671 tok) a přidat minimax-m2.7:cloud.
Co jsem zkusil / co fungovalo a proč: Nejdřív jsem mylně tvrdil „víc spojení nepomůže, je to sdílený výpočet" — uživatel upřesnil, že jde o kvótu paralelních dotazů. To je správná diagnóza. Řešení: per-provider strop souběžnosti přes SemaphoreSlim. Provider record rozšířen o int? MaxConcurrency (Config.cs); RunTest z providerů se stropem postaví slovník semaforů a předá ho do RunModel. Nový helper Parse(parser, gate, request) získá slot await gate.WaitAsync() před voláním (tedy mimo stopky v ParseAsync, které obalují jen HTTP call) a uvolní ve finally → čekání na slot se nezapočte do měřené latence. config.example.json: ollama maxConcurrency:3, qwen→minimax. Reálný config.json (gitignored): uživatel ho mezitím sám protrimoval na 4 modely; po dohodě přidán jen minimax (→ 5 modelů: glm, deepseek, minimax, haiku, gpt-nano) + maxConcurrency:3. Build čistý.
Běh 5 modelů s gate=3:
| Model | Úspěšnost | Wall median / avg | Tokeny in / out |
|---|---|---|---|
| glm-5.1 (ollama) | 17/17 | 1690 / 1927 ms | 21118 / 2896 |
| deepseek-v4-flash (ollama) | 17/17 | 4894 / 6118 ms | 21654 / 3487 |
| minimax-m2.7 (ollama) | 17/17 | 4815 / 4604 ms | 21969 / 2317 |
| claude-haiku-4.5 (openrouter) | 16/17 | 1064 / 1135 ms | 23668 / 691 |
| gpt-5.4-nano (openrouter) | 17/17 | 3034 / 4003 ms | 20987 / 553 |
Co fungovalo: glm median spadl z 3240 ms (kontence) na 1690 ms, žádné 20–60 s odlehlé hodnoty. minimax 17/17 (čistší než qwen). Pozn.: se 3 ollama modely strop (3) zatím nepřekáží — funguje jako pojistka, kdyby přibyl 4. ollama model. Gotcha: dotnet run --project src/MiniLoop z repo rootu nenajde config.json (relativní k cwd) → spouštět s MINILOOP_CONFIG=src/MiniLoop/config.json nebo z adresáře projektu.
Co zbývá: Zdroje src/MiniLoop/ stále necommitnuté — čeká na výslovnou výzvu. config.example.json má pro ilustraci 6 modelů (vč. gemma4-31b), reálný config.json 5 (bez gemmy) — záměrný rozdíl (template vs. pracovní sada).
2026-06-03 — MiniLoop: test levných/OSS modelů z OpenRouteru
Cíl: Uživatel chtěl projít nabídku ollama cloud (ollama.com/search?c=cloud) + levné/OSS z OpenRouteru a vybrat, co stojí za vyzkoušení. Pak přidat vybrané do configu a pustit test jen na ně (ne přetáčet už známé). Mezitím uživatel sám upravil Program.cs: test teď bere výčet modelů nebo all (test <model...> | test all) — díky tomu jdou nové modely testovat samostatně.
Co jsem zkusil / co fungovalo a proč: Z OpenRouteru (katalog přes curl https://openrouter.ai/api/v1/models, filtr na OSS klíče + cena) vybráno 5 levných instruct (ne-thinking) modelů a přidáno do config.json (provider openrouter): mistralai/mistral-small-3.2-24b-instruct, qwen/qwen3-30b-a3b-instruct-2507, google/gemma-3-27b-it, z-ai/glm-4-32b, openai/gpt-oss-120b. Spuštěno test mistral-small-3.2 qwen3-30b-a3b gemma-3-27b glm-4-32b gpt-oss-120b.
| Model | Cena $/M | Úspěšnost | Wall median / avg | out tok |
|---|---|---|---|---|
| mistral-small-3.2 | 0.075/0.20 | 17/17 | 934 / 1050 ms | 610 |
| gemma-3-27b | 0.08/0.16 | 17/17 | 1413 / 1611 ms | 608 |
| glm-4-32b | 0.10/0.10 | 16/17* | 1905 / 2072 ms | 519 |
| qwen3-30b-a3b | 0.043/0.17 | 16/17* | 1991 / 1866 ms | 588 |
| gpt-oss-120b | levný | 16/17 | 7238 / 12164 ms | 3769 |
* false negative (slovosled se protáhnout/protáhnout se; pečení/pečeni). gpt-oss got = expected (rozvrh sedí, JsonCompare škobrtl na formátu). Žádná skutečná chyba.
Co fungovalo a proč: mistral-small-3.2 je nejlepší poměr ze všech dosud měřených (934 ms median, rychlejší než glm-5.1 1690 ms i haiku 1064 ms; 17/17; out 610 tok; cena pakatel) — potvrzená evropská/česká stopa Mistralu. gemma-3-27b těsně za ním. gpt-oss-120b jediný propadák — reasoning → 7 s a 6× víc out tokenů. Silné potvrzení teze o thinkingu: qwen3-30b-a3b-instruct 1991 ms vs cloud qwen3.5 (thinking) 10852 ms + out=22671 — past byl režim thinking, ne qwen rodina.
Co zbývá: Zvážit prořezání gpt-oss-120b z config.json (propadák). Ollama cloud tipy k případnému pullnutí na nvidia.hell (netestováno): gemini-3-flash-preview, gemma4:26b, nemotron-3-nano:30b; už stažené netestované nemotron-3-super, deepseek-v4-pro. Souhrn čísel: knowledge.md „Levné / OSS modely z OpenRouteru".
2026-06-03 — MiniLoop: test ministral-3 a nemotron-3-nano přes ollama
Cíl: Uživatel chtěl vyzkoušet ještě ministral-3 a nemotron-3-nano přes ollama (mezitím sám prořezal config.json na 5 modelů + nastavil mistral-small-3.2 jako interactive).
Co jsem zkusil / co fungovalo a proč: Oba mají :cloud variantu → žádný GB download, jen registrace pointeru na ollama cloud přes curl POST http://nvidia.hell:11434/api/pull {"model":"…","stream":false} → {"status":"success"}. Vybráno ministral-3:8b-cloud (8b = „latest", 3b by na CZ byl slabý) a nemotron-3-nano:30b-cloud. Přidáno do config.json (provider ollama), spuštěno test ministral-3-8b nemotron-3-nano-30b. Vrácení zpět: curl POST /api/delete {"model":"…"} nebo ollama rm na nvidia.hell.
| Model | Úspěšnost | Wall median / avg | out tok |
|---|---|---|---|
| ministral-3:8b-cloud | 15/17 | 1043 / 1169 ms | 653 |
| nemotron-3-nano:30b-cloud | 16/17 | 2015 / 2277 ms | 7805 |
Výsledek — oba zavrženy, nepřekonaly mistral-small-3.2: ministral-3-8b udělal skutečnou chybu dne v týdnu (každý pátek → cron * * 6 sobota místo * * 5) — u připomínek vážné, na 8b znát; navíc není ani rychlejší než mistral-small (1043 vs 934 ms). nemotron-3-nano-30b má reasoning sklony (out=7805 tok ≈ 12× mistral), 2× pomalejší; jediný FAIL = rozsekání 1,3,5 na tři cron výrazy (rozvrh ekvivalentní, formát ne). Poznatek: pod ~24b instruct klesá spolehlivost cronu, malé „nano" buď chybují, nebo zbytečně reasonují.
Co zbývá: mistral-small-3.2 zůstává vítěz. ministral/nemotron-nano v config.json ponechány pro případné srovnání (lze prořezat). Souhrn: knowledge.md „Malé ollama modely".
2026-06-06 11:01 — Skill /keep: ukládání why, ne jen what
Cíl: Uživatel zaznamenal, že Claude memory.md vzor ukládá u feedback/project paměti i why (**Why:** / **How to apply:**), a ptal se, zda by /keep neměl dělat totéž. Cílem bylo minimalisticky upravit prompt skillu, aby why ukládal — ale jen kde to dává smysl.
Co jsem zkusil / co fungovalo a proč:
- Analýza: Claude vzor přidává why jen podle typu — feedback/project ano, reference/fakt ne.
/keepje ekvivalent reference/fakt vrstvy (terse, auto-load každý tah přes USER.md referenci), why-vrstvou nanobotu je Dream → MEMORY.md. Bloat why ke každému řádku by zdvojnásobil trvalý token cost. - Potvrzeno článkem Nick Babich „MEMORY.md for Claude Code projects" (UX Planet, 2026-05-28): bod #3 „Explain WHY, Not Just WHAT" + #5 „Record Failures and Dead Ends" — ale jeho příklad why (
Switched to Redis because…) je rozhodnutí, ne fakt → potvrzuje úzkou variantu. - Iterace promptu (3 kola): (1) přidán why-řádek do kroku 2 + mantinel do Rules, s 2 few-shot příklady (decision, dead-end). (2) Po dotazu na model: lokální modely zavrženy, reálně běží Ollama Cloud (kimi-k2.6, deepseek-v3.2, qwen, glm-5.1) + OpenRouter (sonnet/haiku/gemini) — silné modely → fakt-vs-rozhodnutí zvládnou zero-shot → oba příklady odebrány. (3) Přidáno: pokud je vstup rozhodnutí/dead-end bez důvodu, model se jednou doptá na why (decline/self-evident → ulož bez něj).
- Finální změny v
skills/keep/SKILL.md: krok 2 Write protokolu (3 pravidla pro why) + 1 mantinel v Rules. Žádná změna formátu souboru (pořád 1 řádek = 1 záznam), why jen u rozhodnutí/preferencí/dead-endů.
Nasazení: ověřeno server == lokál HEAD před přepisem (žádná Dream úprava nezahozena) → rsync -av skills/keep/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/keep/. Vlastník nanobot:nanobot, příklady ověřeně pryč. Skilly se čtou každý tah bez restartu.
Co zbývá: Sledovat, jestli silné modely doptávání na why nepřehánějí (riziko nagování u hraničních „rozhodnutí"). Pokud ano → zúžit trigger nebo doplnit 1 negativní příklad.
2026-06-06 11:16 — Nový skill python (coding conventions)
Cíl: Dát nanobotovi pravidla pro práci s Pythonem — uv tool, type hints, linter/formatter, PEP + clean code.
Co jsem zkusil / udělal:
- Založen
skills/python/SKILL.md. Tělo vychází z.claude/rules/python.md(použito celé) + doplněna sekce Tooling (uv,ruff format,ruff check --fix, definice „done"). - Z
.claude/rules/clean-code.mdpřidány jen body zvyšující čitelnost/strukturu: boolean flag args, CQS, param count 0–3, fail fast / guard clauses, max zanoření, magic numbers → konstanty. Designové body (composition, DI, immutability) vynechány. - Na žádost uživatele vyházeny věci, které modely dělají samy: casing (snake_case/PascalCase), 4-mezery, f-stringy, řazení importů. Naming + Imports sekce zrušeny, Style zúžen na line-length 120.
descriptionschválně vágní („Use for anything involving Python code") — ať se skill aktivuje skoro vždy.
Co fungovalo a proč: rsync -av skills/python/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/python/. Vlastník nanobot:nanobot ověřen. Skilly se čtou každý tah bez restartu.
Co zbývá: Sledovat, jestli vágní description nezpůsobuje načítání i mimo Python kontext (přijatelné — „lepší načíst zbytečně než minout").
2026-06-06 11:20 — Nový skill bash (shell conventions)
Cíl: Stejně jako python — převést .claude/rules/bash.md na nanobotí skill.
Co jsem udělal:
- Založen
skills/bash/SKILL.mdz.claude/rules/bash.md. - Vyházeno: Naming sekce (casing — modely trefují samy),
[[ ]]vs[ ](default), zmínka ocheck-bash.shhooku (hook tohoto repa, na serveru irelevantní),paths:frontmatter (Claude Code feature). - Ponecháno to, kde modely v bashi reálně chybují / projektový standard: strict mode, double-quoting, local/readonly + návrat přes stdout, arrays vs IFS,
command -vmístowhich, stderr, mktemp+trap, script_dir idiom, shellcheck, hook exit kódy 0/2. descriptionvágní, ať se aktivuje široce.
Co fungovalo: rsync -av skills/bash/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/bash/. Vlastník nanobot:nanobot ověřen.
Co zbývá: nic.
2026-06-07 — Měření rychlosti glm-5.1 vs minimax-m3 (Ollama nativní streaming)
Cíl: Tvrdá data na dotaz „je minimax línej?" — porovnat time-to-first-token, rychlost generování a celkovou dobu odpovědi mezi glm-5.1:cloud a minimax-m3:cloud na reálné úloze typu /remind list.
Co jsem zkusil:
- Journal nestačí.
journalctl --user -u nanobotloguje jenProcessing message → Tool call → Response(časové značky). Žádné počty tokenů, žádné tok/s, žádný TTFT. Wall-clock z journalu navíc míchá běh nástrojů (uv runcold-start ~2–7 s) a víc kol tool-callů → na rychlost modelu nepoužitelné. (Z journalu jen kontext: naminimax-m3přepnuto poprvé 2026-06-05, dnes 09:03; jeden reálný/remind listpod m3 trval wall 37 s včetně 2 tool-callů.) - Ollama
:cloudnativní timing fields jsouNone(eval_duration,prompt_eval_duration,load_duration) — výpočet jede přes Ollamin cloud, lokální server nanvidia.hellje nezná. Vrací jeneval_count(out tok) +total_duration. - Řešení = streaming
/api/chat. Skript/tmp/bench_ollama.py(na serveru): TTFT = čas prvního content chunku, generace =eval_count / (total − TTFT), identický prompt (/remind listformátování 8 položek do češtiny), 3 běhy/model.
Naměřeno (per běh):
| Model | TTFT | Total wall | Out tok | Generace* |
|---|---|---|---|---|
| glm-5.1 | 5,50 / 7,78 / 5,93 s | 6,43 / 8,83 / 7,15 s | 1209 / 1729 / 1511 | ~1290 / 1650 / 1240 tok/s |
| minimax-m3 | 5,98 / 7,25 / 6,75 s | 9,76 / 11,36 / 11,28 s | 457 / 419 / 428 | ~121 / 102 / 95 tok/s |
* eval_count / (total − TTFT).
Co fungovalo a proč:
- TTFT srovnatelný (~6–7 s u obou) — minimax neprohrává startem, oba mají podobný cloud cold start.
- Celková doba: minimax ~50 % delší (medián ~10,8 s vs ~7,5 s glm), a to přestože generuje 3–4× MÉNĚ tokenů (~430 vs ~1480). To je hlavní důkaz „lenosti" minimaxu.
- Generace tok/s: minimax ~95–120 tok/s je čisté číslo (streamuje token po tokenu). GLM hodnotu „1300 tok/s" NEbrat doslovně — je to artefakt cloud bufferu (glm flushne odpověď v dávce po TTFT). Robustní srovnatelná metrika je end-to-end průtok = out/total: glm ~198 tok/s vs minimax ~40 tok/s → glm ~5×.
Závěr: Intuice potvrzena tvrdě — minimax-m3 je výrazně línější: pomalý decode (~100 tok/s) + delší celková doba i u kratšího výstupu. Na interaktivní úlohy je glm-5.1 jednoznačně svižnější. Pozn.: glm je upovídaný (1200–1700 tok vs ~430), ale i tak hotový dřív.
Jak vrátit zpět: nic se na serveru nemĕnilo (jen read-only měření + dočasný /tmp/bench_ollama.py).
Co zbývá: nic.
2026-06-07 09:54 — Znalostní báze modelů pro nanobot agenta (workspace/knowledge/)
Cíl: Dát nanobot agentovi na serveru znalostní bázi o modelech, ze které může těžit při dotazech typu „jaký model na kódování / na zpracování fotky" — fakta a naměřené hodnoty, žádná hotová doporučení (volba je na agentovi).
Co jsem zkusil:
- Vytáhl reálné presety ze serverového
config.json→model_presets(7 presetů: glm-5.1 default, minimax-m3, kimi-k2.6 na ollama; sonnet, haiku na openrouter; gemini-flash na gemini; gemini-flash-lite na openrouter). Liší se od lokálníhomodels.md, který řešil jen 3 Ollama Cloud modely. - Konsolidoval do
knowledge/models.md(CZ, facts-only): tabulka presetů (provider, model id, kontext, multimodalita), přímé měření rychlosti na Ollama Cloud (glm ~198 tok/s vs m3 ~40 tok/s, m3 o ~50 % pomalejší), AA profil verbozity, rozdíly ve schopnostech (multimodál, HLE, kódování, dlouhý kontext), caveaty (vendor vs produkce, prompt caching jen openrouter/anthropic/bedrock, ollama kvóta 3 paralelní), kontextová okna, appendix s MiniLoop měřeními (jasně označeno jako jiný kontext, ne agent presety). knowledge/README.mdjako rozcestník (vzor podledevelop/README.md).- Napojení: přidána sekce
## Knowledge basedoAGENTS.md(bootstrap, čte se každý tah) odkazující naknowledge/README.md„read on demand" — stejný vzor jako existující odkaz nadevelop/README.md. - Kanonická kopie v repu
knowledge/; nasazenorsyncem do~/.nanobot/workspace/knowledge/. AGENTS.md staženo, upraveno lokálně, posláno zpět.
Co fungovalo a proč: Vlastník na serveru nanobot:nanobot, ověřeno. knowledge/ se čte bez restartu (bootstrap soubory i workspace soubory čte agent při tahu). AGENTS.md napojení znamená, že agent o bázi ví a sáhne tam on-demand.
Jak vrátit zpět: ssh nanobot@nanobot.hell 'rm -rf /home/nanobot/.nanobot/workspace/knowledge' + smazat sekci ## Knowledge base z ~/.nanobot/workspace/AGENTS.md.
Co zbývá: nic. Případně až nanobot bázi reálně použije, ověřit, že odpovídá rozumně.
Oprava (09:5x): Sekce ## Knowledge base v AGENTS.md byla moc konkrétní (vyjmenovávala modely). Zobecněna — knowledge/ je obecná báze, modely uvedeny jen jako příklad; konkrétní výčet zůstává v knowledge/README.md (read on demand).
2026-06-07 13:14 — Detach: volitelný výběr modelu pro background task
Cíl: Umožnit, aby detached task běžel na explicitně zvoleném modelu (jiném než default) — background = latence nebolí, vyplatí se silnější model. Když se model neuvede → default (zpětně kompatibilní).
Co jsem zkusil:
- Ověřil v upstreamu (
tmp/nanobot-upstream), žeNanobot.run()model override nepřijímá, aleAgentLoop.set_model_preset(name)existuje (přesně to dělá/model <preset>v chatu) a hodíKeyErrorna neznámý preset. - Zamítl variantu „zdědit aktivní model na kanálu":
/modelpřepnutí je in-memory, globální (ne per-kanál), neperzistuje a daemon (samostatný proces) ho nevidí; šlo by jen patchem upstream_build_runtime_context→ fork balíčku. Příliš drahé. Rozhodnutí s uživatelem: explicitně v požadavku, jinak default; resolve názvu fuzzy proticonfig.json. tasks_common.py:load_preset_names()(čtemodelPresetsimodel_presets— server má na disku snake_case),resolve_preset(token, names)(exact CI → unikátní substring →KeyErrornot found / ambiguous),build_task_content(..., model=…)přidá frontmatter řádekmodel:jen když je zadán.format_resultukážemodel:v meta řádku.create-task.py:--model(optional) → resolve při captue, naKeyErrorvypíše hlášku na stderr areturn 1(fail-fast, nic nezapíše).tasks-daemon.py:preset = fm.get("model"),run_agent(..., preset)→bot._loop.set_model_preset(preset)předrun(). LogSTART … preset=<x|default>.- Dokumentace:
SKILL.md(krok „Optional model" + failure handling pro unknown/ambiguous),architecture.md(frontmattermodel:, resolve při captue, daemon switch). Testy: +9 vtests/test_tasks_common.py(43 passed). - Nasazeno
rsyncem na server, vlastníknanobot:nanobotověřen.
Co fungovalo a proč:
- Resolve proti reálnému serverovému configu: 7 presetů (
gemini-flash, gemini-flash-lite, glm, haiku, kimi, minimax, sonnet),kimi→kimi,flash-lite→gemini-flash-lite,gemini→ambiguous,nonsense→not found. - Fail-fast:
create-task.py --model nosuchmodel→ exit 1, hláška se seznamem,inbox/beze změny. - E2e: task
model-test-kimi(--model kimi, goal „reply PONG") doběhl za 3 s, logSTART … preset=kimi, výsledekPONG,read-taskukázalmodel: kimi. Tím ověřeno, žebot._loop.set_model_presetfunguje i s nainstalovaným balíčkem nanobota (ne jen v upstream klonu). Testovací task zarchivován. - Gotcha potvrzen: serverový
config.jsonmá top-level klíčmodel_presets(snake_case), ne camelCase;agents.defaults.modelPresetje naopak camelCase. Protoload_preset_namesčte oba tvary — nebýt toho, vracelo by prázdno a každý--modelby failoval.
Jak vrátit zpět: přenasadit předchozí verzi skillu ze staré git revize: git checkout <rev> -- skills/detach + rsync -av --exclude __pycache__ skills/detach/ nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/skills/detach/. Bez --model se chování nemění, takže rollback není urgentní.
Co zbývá: Návrh do decisions.md (load-bearing) — čeká na souhlas autora. Zvážit zvednutí 15-min timeoutu u explicitně zvoleného „heavy" modelu (M3/Kimi jsou pomalé/verbózní) — samostatné rozhodnutí.
2026-06-07 13:xx — Detach: zvednut timeout 15 → 45 min
Cíl: Background tasky na silnějších modelech (M3/Kimi — na Ollama Cloud pomalé/verbózní) můžou u hlubokého researche přerůst 15 min. Uživatel nemá v příkazu řešit timeout, tak zvednout globální strop.
Co jsem zkusil:
- Ověřil, že systemd
tasks-daemon.servicenemá vlastní start-timeout:Type=oneshot→TimeoutStartUSec=infinity,RuntimeMaxUSec=infinity(systemctl --user show). Jediný strop je tedy Pythonasyncio.wait_for(TIMEOUT_SECONDS). TIMEOUT_SECONDS = 15*60 → 45*60vtasks-daemon.py; sjednoceny zmínky „15 min" v docstringu,architecture.md,knowledge.md.- Nasazeno
rsyncem, vlastníknanobot:nanobot, konstanta na serveru ověřena. Bez restartu (oneshot = nový proces na každý běh).
Co fungovalo a proč: Timeout je čistě pojistka proti zaseknutému běhu — reálný task doběhne dřív; delší strop nic nestojí kromě toho, že zaseknutý task déle drží frontu (u single-user low-volume OK). 45 min dává headroom pro deep research na silném modelu.
Jak vrátit zpět: v tasks-daemon.py zpět na 15 * 60 + rsync.
Co zbývá: nic. Sledovat, zda 45 min stačí / nepřehání.
2026-06-07 17:51 — Měření rychlosti glm-5.1 vs minimax-m2.7 (Ollama nativní streaming)
Cíl: Na dotaz „jak je m2.7 rychlý oproti glm-5.1" dát tvrdá apples-to-apples data — m2.7 zvažovaný jako náhrada za m3 (BLOCKED, viz knowledge.md). Předchozí streaming benchmark (téhož dne) měřil jen m3, ne m2.7.
Co jsem zkusil:
/api/tagsnanvidia.hellukázal jenminimax-m3:cloud(m2.7 z katalogu zmizel — Ollama Cloud ho nahradila m3).POST /api/pull {"model":"minimax-m2.7:cloud"}ale vrátil{"status":"success"}→ cloud pointer jde dotáhnout, jen není v defaultním seznamu.- Streaming
/api/chatbenchmark (/tmp/bench_ollama2.py), identický/remind listprompt (8 položek do češtiny), 5 kol prokládaně (každé kolo glm pak m2.7, ať sdílí stejnou zátěž cloudu) + mediány. TTFT = 1. content chunk; e2e =eval_count / total.
Naměřeno (mediány, 5 kol):
| Model | TTFT | Total wall | Out tok | e2e (out/total) |
|---|---|---|---|---|
| glm-5.1 | 15,4 s | 18,3 s | 1558 | ~91 tok/s |
| minimax-m2.7 | 9,2 s | 11,3 s | 351 | ~28 tok/s |
Co fungovalo a proč:
- Absolutní čísla nafouklá oproti rannímu m3 měření (glm TTFT 15 s vs ~6 s ráno) — cloud byl vytížený. Proto bráno jen jako poměr v rámci běhu, prokládání kol drží srovnatelnost.
- Decode m2.7 ~3× pomalejší než glm (~28 vs ~91 tok/s e2e) — dokonce horší než m3 (~40 tok/s). Líný decode je rys celé MiniMax řady, m2.7 to nezlepšil.
- m2.7 doběhne dřív ve wall-clocku (11 vs 18 s) jen díky terseness — vygeneroval ~4,5× méně tokenů (351 vs 1558). glm je upovídaný, ale per-token mnohem rychlejší. Konzistentní s MiniLoop 2026-06-03 (m2.7 4815 ms vs glm 1690 ms, tam podobné token county → glm vyhrál decodem).
Závěr: m2.7 není pro agenta rychlostní výhra — pomalý decode bolí u delších výstupů (tool args, kód). Wall-clock výhoda platí jen pro krátké odpovědi. Pro background deep-research drž glm-5.1 / Kimi.
Jak vrátit zpět: read-only měření + dočasný /tmp/bench_ollama2.py. Na ollamě přibyl pointer minimax-m2.7:cloud (ollama rm minimax-m2.7:cloud na nvidia.hell ho odebere, pokud nemá zůstat).
Co zbývá: ověřit m2.7 v reálném detach běhu (jestli neskončí ve smyčce jako m3).
2026-06-07 18:26 — Detach list: odrážkový výpis místo markdown tabulky + reconciliace driftu
Cíl: /detach list na minimax-m2.7 rozsypal formát — Running se vyrenderoval jako tabulka, Done/Failed jako syrové | … | roury (na GLM OK). Příčina: výstup relayuje LLM token po tokenu a markdown tabulka má dálkovou závislost (hlavička + |---|); slabý model u 2./3. sekce hlavičku zahodí. Cíl = formát robustní vůči LLM relay.
Co jsem zkusil:
render_table→render_listvtasks_common.py: dvouřádková odrážka na úkol (slug · čas · stáří + odsazený popis), žádná tabulková gramatika. Popis se vynechá, když chybí# Goal. JSON zamítnut — jen posouvá křehkost na model, který ho převádí pro člověka.list-tasks.pypřepnut narender_list;SKILL.mdlistzpřísněn na „output verbatim, do not convert to a table".- Testy přepsány (
test_render_list_*, +omits_goal_line_when_empty) → 57 passed. Lokální render proti dočasnému workspace ověřil starý i nový timestamp, soubor bez Goalu i neparsovatelný filename. - Drift na serveru (uživatel varoval): nanobot si sám opravil
NameErrorvtasks-daemon.py— repo voláLOG.parent.mkdirna ř. 152, aleLOGnebyl importován (zaneseno minulou session). Server záplatoval lokálnímfrom tasks_common import LOG as _LOG. Dotaženo do repa čistší formou —LOGpřidán do top-level importu (ř. 37), což serverový workaround subsumuje. - Deploy
rsyncem, vlastníknanobot:nanobot, deployedlist-tasks.pyověřen proti reálným serverovým taskům (odrážky, truncation…na 80 zn., žádné|).
Co fungovalo a proč: Odrážkový seznam přežije relay, protože každý - … řádek stojí sám — není hlavička, jejíž ztráta rozbije vše pod ní. Je to zároveň drátový i zobrazovací formát, takže odpadá transformační krok, kde slabý model selhával.
Jak vrátit zpět: git revert commitu se skill změnami + rsync repa zpět na server (vrátí i render_table). Pozn.: vrácení daemon importu by znovu zavedlo NameError — opravu importu LOG ponechat.
Co zbývá: end-to-end potvrzení v chatu na minimax-m2.7 i GLM (uživatel ověří interaktivně).
2026-06-07 19:33 — tasks-daemon zaseknutý v start-limit-hit + hardening retry
Cíl: Uživatel hlásil problém s user systemd službami tasks-daemon.path / tasks-daemon.service. Zjistit příčinu, navrhnout fix, nasadit po schválení.
Co jsem zkusil / zjistil:
systemctl --user status→ obě unityfailed,.servicesResult: unit-start-limit-hit, „Start request repeated too quickly".stderr.log+ journal: v 18:19 daemon 5× po sobě spadl naNameError: name 'LOG' is not defined(tasks-daemon.py:152) → za <2 s vyčerpal default rate-limit (StartLimitIntervalSec=10s,Burst=5) → systemd zalatchoval.servicei.path.- Časová osa: do 17:29 OK (4× done); 17:44 task na
minimax; 18:19:14 SIGTERM (status=143) — žádný timeout v unitu (předchozí běhy i 1553 s doběhly), takže externí stop (deploy fixu); 18:19:36–38.path(level-triggered, inbox neprázdný) hned restartoval → 5× NameError → latch. - Klíčové: ten
NameErroruž byl opravený předchozí session (importLOGna ř. 37, viz záznam výše) a fix byl na serveru nasazený. Ruční test daemonu s korektní PATH →exit=0. Příčina pádu pryč; jediný zbytek = systemd zaseknutý vefailed, který se sám nezotaví.
Co fungovalo a proč:
- A — recovery:
systemctl --user reset-failed tasks-daemon.service tasks-daemon.path+restart tasks-daemon.path→.pathzpětactive (waiting). Inbox prázdný, nic se hned nespustilo. - B — orphan: smazán
failed/2026-06-07_18_28_43_…-gemini.md(vznikl 18:28 po latchi, nikdy nezpracován — bez## Resultsekce). - C — hardening unitu (
skills/detach/systemd/tasks-daemon.service): přidánoRestart=on-failure+RestartSec=60(delay mezi pokusy po pádu; čistý exit 0 i SIGTERM od systemd nerestartují) aStartLimitIntervalSec=1800+StartLimitBurst=20(až 20 pokusů / 30 min, pak pauza + auto-resume jak okno klouže — už žádný permanentní latch ani ručníreset-failed). Doc ověřena:man systemd.service— proType=oneshotjsou zakázané jenRestart=always/on-success,on-failurepovolený. - Deploy
rsyncem jensystemd/podadresáře (změna izolovaná do unitu),daemon-reload, vlastníknanobot:nanobot.systemctl showpotvrdilRestart=on-failure / RestartUSec=1min / StartLimitIntervalUSec=30min / StartLimitBurst=20.
Jak vrátit zpět: git revert commitu se změnou tasks-daemon.service + rsync systemd/ zpět na server + daemon-reload. Recovery (A) a smazaný orphan (B) se nevrací.
Co zbývá: End-to-end potvrzení (nový detach task projde .path → drain → done) — uživatel ověří interaktivně; daemon na prázdném inboxu už ověřen (exit=0).
2026-06-10 07:35 — git cleanup: přestat trackovat gitignored soubory na serveru
Cíl: .gitignore v serverovém repu (~/.nanobot/workspace) přibyl pozdě — __pycache__/*.pyc a MEMORY.md.bak se dostaly do gitu dřív, takže je ignore neřešil a pořád byly trackované. Vyndat je z gitu, nechat na disku.
Co jsem zkusil:
git ls-files -i -c --exclude-standard→ 11 souborů trackovaných i přes.gitignore: 10×*.pycv__pycache__/(skillsdetach/remind) +memory/MEMORY.md.bak.git ls-files -i -c --exclude-standard -z | xargs -0 git rm --cached→ odstranění z indexu, soubory na disku zůstaly (ověřenols).- Commit
f93c1cfjen s těmito removaly (explicitní pathspec) — repo mělo rozdělanou autonomní práci (Dream procesor:cron/jobs.json,memory/history.jsonl, skriptyremind/), té se commit nedotkl. Po commitugit ls-files -i -c --exclude-standard= 0.
Co fungovalo a proč: .gitignore ignoruje jen netrackované soubory; co už je v indexu, musí ven přes git rm --cached (smaže z indexu, nechá na disku). Od teď se nové __pycache__/ už necommitují.
Jak vrátit zpět: git revert f93c1cf (znovu je začne trackovat).
2026-06-10 07:55 — Revize přepsaného remind skillu (YAML → SQLite)
Cíl: Uživatel nechal nanobota přepsat /remind skill z reminder.yaml na SQLite. Zkontrolovat výsledek, posoudit funkčnost, otestovat, navrhnout změny (vč. textu SKILL.md). Nic neměnit bez souhlasu.
Co jsem zkusil:
- Stáhl serverový skill (
rsync) dotmp/server-remind/, přečetlSKILL.md,db.py,remind_edit.py,remind_send.py,random_times.py+ všechny testy. - Ověřil reálný stav serveru: schema
reminders.sqlite(nové:days_filter/from_date/until_date), 14 reálných připomínek migrováno (25at, 6 cron, 9 random),reminder_fires=0. - Pustil test suite na serveru: 43 passed.
- Read-only kontrola deployed senderu proti kopii prod DB (
REMIND_DB, bez odeslání) — všech 9 random se spočítá bez výjimky, nic chybně due. - Live test doručení:
remind_edit.py add --atna +2 min (id=15), ověřenoreminder_fireszápis('at', delivered, 07:54:01)+ DELIVER v logu + reálný příchod na Telegram (potvrdil uživatel), pakremove --keyword(soft-delete).
Co fungovalo a proč: Přechod na DB je funkčně nasazený a doručování jede. Nalezené defekty:
- P1 schedule_type collision:
schedule_{at,cron,random}mají vlastní AUTOINCREMENT id → překryv (at 1–25, cron 1–6, random 1–9). UNION-ALL inference vremind_send.main()označí každý cron/random fire jako'at'→reminder_fires.schedule_typešpatně + dedup pro cron/random nefunkční (maskuje jen 60s tolerance). Fix: každá_due_*vrací svůj typ. - P1 stale bootstrap: serverové
AGENTS.md+TOOLS.mdpořád mluví oreminder.yamla starém formátureminder.log(auto-load každý tah). - P2 log regrese:
log_operationpíše UTC + míchá ADD/EDIT/…/DELIVER;TOOLS.md„co dnes přišlo“ čeká Prague-time delivery-only. - P3: mrtvý
import yaml/pyyaml dep, ignorovaný sloupectimezone, hardcodedCHAT_ID, duplicitní text neřešitelný přes keyword (chybí--id),__import__("datetime"),--replace-schedulesbez schedule → němá připomínka. - SKILL.md text: matoucí „python3 required“, chybí instrukce odpovídat jazykem uživatele a jak řešit read-back doručení.
- Repo desync: lokální
skills/remind/je pořád YAML verze +IMPROVEMENTS_REPORT.md+reminder.example.yaml;knowledge.md/remind sekce neplatná.
Co zbývá: Uživatel odsouhlasil rozsah oprav (P1 schedule_type, P1 bootstrap docs, P3 cleanupy, repo+knowledge sync); P2 (reminder_fires dotaz vs. čistý delivery-log) ještě nerozhodnuto.
Jak vrátit zpět: Test připomínka (id=15) už odstraněna (soft-delete). Žádná jiná změna na serveru neproběhla.
2026-06-10 08:10 — Implementace oprav remind skillu (po odsouhlasení)
Cíl: Provést odsouhlasené opravy z revize výše: P1 schedule_type, P1 bootstrap docs, P2 read-back přes reminder_fires, P3 cleanupy, repo+knowledge sync.
Co jsem zkusil / udělal:
- remind_send.py: každá
_due_{at,cron,random}vracíschedule_type; smazána chybná UNION-ALL inference vmain(). Odstraněn mrtvýimport yaml+ deppyyaml.CHAT_ID→_telegram_config()čtechannels.telegram.allowFrom[0]z configu, fallback konstanta. - remind_edit.py: helper
_resolve_one(výběr přes--idnebo--keyword, ambiguous vypíše ids);--idpřidáno k remove/edit/enable/disable; guard na--replace-schedulesbez nového schedule; nový subcommanddelivered [--since](čtereminder_fires, Prague time);from datetime import datemísto__import__; depcroniteronly. - SKILL.md: instrukce odpovídat jazykem uživatele, dokumentace
delivered+--id+ duplicit, oprava matoucího Environment. - Testy: +5 (schedule_type collision, --id disambiguace, resolve vyžaduje id/keyword, replace-schedules guard, delivered) → 48 passed lokálně i na serveru.
- Nasazení: ověřeno, že server skill mezitím nikdo nesáhl (diff = jen mé změny),
rsyncna server, ownernanobot:nanobot, server pytest 48 OK, smokelist+deliveredproti reálné DB OK.deliveredukázal reálné doručení „Panama" 08:02 — potvrdilo, že stará verze zapsala random odpal jakoschedule_type='at'(P1 bug v praxi). - Bootstrap:
AGENTS.md+TOOLS.mdna serveru —reminder.yaml→ SQLitedb/reminders.sqlite, sekce oreminder.logpřepsána nadelivered/reminder_fires. Push ověřen. - Repo:
skills/remind/synced z deploye, smazányreminder.example.yaml+IMPROVEMENTS_REPORT.md(commitb244c01).knowledge.md/remind sekce přepsána.
Co fungovalo a proč: Skilly se čtou bez restartu (exec subprocess + bootstrap fresh každý tah), takže fix je živý okamžitě. Dedup pro cron/random teď reálně funguje (správný schedule_type), ne jen díky 60s toleranci.
Co zbývá: Jeden historický řádek reminder_fires (reminder 7, 08:02) má pořád schedule_type='at' z bugu — oprava přes přímý prod UPDATE byla blokována auto-classifierem (neautorizovaný prod write), čeká na svolení uživatele. Kosmetické (znovu se neodpálí). Kandidáti do decisions.md (čekají na přeformulování autorem): úložiště = SQLite; read-back i audit = reminder_fires.
Jak vrátit zpět: Skill: git revert b244c01 + rsync zpět na server. Bootstrap: serverové AGENTS.md/TOOLS.md vrátit na reminder.yaml formulaci (DB ale existuje, takže to nedává smysl). DB schema/data beze změny.
Dodatek (08:13): Po svolení uživatele opraven zaseknutý audit řádek — UPDATE reminder_fires SET schedule_type='random' WHERE reminder_id=7 AND fire_time='2026-06-10T08:02:00' (1 řádek). Tím je audit konzistentní s fixem.
2026-06-10 08:50 — Code review remind skillu + cleanup (P1+P2+P3)
Cíl: Detailní revize skillu /remind, ověřit shodu lokál↔server, navrhnout a po schválení provést zlepšení (čistota, čitelnost, normy).
Co jsem zkusil / udělal:
- Review: stáhl serverovou verzi,
diffvšech 5 skriptů + SKILL.md → lokál identický se serverem. Nálezy rozděleny P1/P2/P3, plán odsouhlasen uživatelem (rozsah: vše;listnemá vracet JSON). - P1 — korektnost:
cmd_listvypisoval pozici{idx}., ne skutečné DBid→ agent zlistčetl špatné číslo pro--id. Přepsáno na čitelný formát#<id> text [status]+ odsazené schedule řádky, HH:MM okno, prázdný store →(no active reminders). Helper_schedule_lines. SKILL.md popisuje nový formát místo neexistujícího „JSON".db.log_operation(details: str)→str | None(volá se sNone). - P2 — struktura:
cmd_editvalidace (prázdný text,_build_random) přesunuta předBEGIN(žádná otevřená transakce na early-return). Duplicitní_parse_window/_hhmm_to_minutesvremind_edit.pysmazány → reuseparse_windowzrandom_times.py(publikováno odebráním podtržítka)._build_randomse volá jen jednou —_insert_schedulesdostává hotovýrandom_cfg. - P3:
remind_send._now→_now_prague() -> datetime(konec kolize sremind_edit._now, který vrací str);_telegram_config()čteno jednou vmain()+ early-return na prázdnédue;cmd_deliveredf-string SQL → dvě parametrizované query;_find_by_keywordescapuje LIKE wildcardy +ESCAPE; shebang obou skriptů →uv run --script. Testy: odstraněn nepoužitýcapsys,_run_sendkorektně zachytává/obnovuje funkce, +3 testy (randomdays_filtere2e, retry po failed fire,delivereddefault „dnes"). - Verifikace: lokálně 51 passed. Deploy
rsynccelý adresář, ownernanobot:nanobot. Server pytest 51 passed. Smoke produkčnílist(reálná id, nový formát),_due_randomdotaz proti reálné DB OK (9 random reminderů). Ověřeno, že produkční schémaschedule_randommádays_filter/from_date/until_date.
Co fungovalo a proč: Skilly se čtou bez restartu. reminder_cron.log mtime 07:36 (před deployem) = od deploye crontab sender nezapsal žádnou novou chybu → běží čistě. Staré traceby v logu pochází z dávno mrtvé verze (_process_reminder, sloupce days/start_date/end_date, ROLLBACK v main) — irelevantní.
Co zbývá / gotcha:
log_operationignorujeREMIND_DB— píše vždy do reálnéhoworkspace/log/reminder.logpřes__file__-relativní cestu. Spuštění test suite na serveru proto zapsalo 24 fixture řádků (timestamp2026-06-10T06:47:32) do reálného logu. Poučení: testy spouštět jen lokálně. Úklid logu (odstranit 24 řádků + truncate staré traceby vreminder_cron.log) byl blokován auto-classifierem (neautorizovaný prod write) — čeká na svolení uživatele.
Jak vrátit zpět: git revert <commit> skillu + rsync předchozí verze na server. DB schema/data beze změny.
2026-06-10 09:50 — Remind skill úklid: rename remind_edit.py→remind_cli.py, edit-guidance, hybrid SKILL.md
Cíl: Tři papercuts najednou (jeden deploy): (1) zavádějící název remind_edit.py (dělá i list/delivered, read-only); (2) agent při opravě textu maže+vytváří záznam znovu, místo edit --text (skript to už uměl, jen SKILL.md k tomu nenaváděl); (3) SKILL.md duplikoval výčet flagů s argparse → drift. Plán: plans/ (schválen v plan mode).
Co jsem zkusil / udělal:
- A. Rename
git mv remind_edit.py → remind_cli.py+test_remind_edit.py → test_remind_cli.py(import +remind_cli.*+ argv[0]). Cross-importy nedotčené (remind_sendimportuje jen zdb/random_times). Crontab nezměněn — spouštíremind_send.py. - B. Dedup:
_minutes_to_hhmmbyl doslova zkopírovaný v cli i send → přesunut dorandom_times.pyjako veřejnéminutes_to_hhmm, obě kopie smazány, importy upraveny. - C. Edit-guidance + test: SKILL.md explicitně „oprava textu =
edit --id <n> --text, NIKDY remove+add"; nový testtest_edit_text_by_id(pokrývá dřív netestovanou--idvětev editace). - D. SKILL.md → hybrid: zachován NL→příkaz mapping + behaviorální kontrakt (formát
list, JSON mutace, keyword/id ambiguous,delivered); vyčerpávající výčet flagů →<cmd> --help. Řeší drift z memory.md. - Lokální změny delegovány na sonnet agenta; deploy + verifikaci řídil opus.
Co fungovalo a proč:
- Lokálně 52 passed (51 + nový test).
- Server = zdroj pravdy:
rsyncserver→tmp/server-remind/,diffvšech 10 zdrojů proti HEAD → identické, žádná autonomní divergence (Dream procesor nesahal) → deploy bezpečný. - Deploy
rsync(exclude__pycache__/.pytest_cache/.venv), staleremind_edit.py+ starý test explicitněrm(rsync bez--deleteje nechává). Ownernanobot:nanobotOK. - Server
remind_cli.py --help+list(reálná data) OK;remind_edit.pypryč; crontab dálremind_send.py. - Živý edit test (bez Telegram fire):
add --at 2020-01-01(v minulosti → nikdy due),edit --id 17 --text "TEST opraveny text"→ text změněn,atschedule (id 27) zachován, pakremove --id 17(úklid).
Co zbývá: Commit + push (tracking zvlášť dle konvence). tmp/server-remind/ je v .gitignore.
Jak vrátit zpět: git revert <commit> skillu + na serveru rsync předchozí verze a mv remind_cli.py remind_edit.py zpět (DB beze změny).
2026-06-13 07:48 — note skill: zákaz vymýšlení tagů
Cíl: Odstranit nešvar, kdy agent při zakládání poznámky automaticky vymýšlí tagy z obsahu. Tagy se mají přidat jen když je uživatel explicitně napíše (první token za triggerem).
Co jsem zkusil: Příčina čistě v textu skills/note/SKILL.md — skript note.py je v pořádku (--tags volitelný, default [], žádné auto-tagování). Svádějící formulace: hlavička „extract tags", věta „Tags are created automatically on first use", krok 1 Write protocolu „Extract inline tags". Upravil jsem všechny tři: hlavička → „take only explicitly-typed tags"; do Tag protocolu přidáno tvrdé pravidlo, že tagy pochází jen z prvního tokenu, nikdy se neodvozují z obsahu; přeformulováno „DB accepts any new tag without prior registration — not a license to make up tags"; krok 1 → „Take … only … tags field stays empty — never fill it from the content".
Co fungovalo a proč: Server↔lokál před úpravou shoda (diff prázdný). Nasazeno rsync -av skills/note/ nanobot@nanobot.hell:…/skills/note/, vlastník nanobot:nanobot ověřen. Skilly se čtou každý tah bez restartu.
Co zbývá: Verifikace reálným chováním (poznámka bez explicitního tagu → žádné tagy). Commit tracking zvlášť.
Jak vrátit zpět: git revert <commit> + rsync předchozí verze SKILL.md na server. DB beze změny.
2026-06-13 10:25 — Display ID (přečíslování on-the-fly) do /remind a /bookmark
Cíl: Přenést mechanismus z /note (sekvenční display ID 1..N počítané on-the-fly, nezávislé na rozházených interních DB id, přečíslování po každém smazání → snazší mazání/úpravy podle krátkého čísla) do /remind a /bookmark. Uživatel řekl „/article", ale to je čistě sumarizační skill bez seznamu — potvrzeno, že myslel /bookmark. Plán: plans/ (schválen v plan mode).
Co jsem zkusil / udělal:
- Vzor z note (zrcadlit, ne vymýšlet): helper vrací seřazené interní id aktivní množiny → výpis postaví
{interní_id: pořadí}→ mutace berou display ID,idx = n-1, bounds-check, přeloží na interní id. - remind (
remind_cli.py,forecast.py): nový_active_display_order(řazeníORDER BY id);cmd_listtiskne#{display}; load-bearing:--idu remove/edit/enable/disable přeznačeno z interního DB id na display ID +--keywordbeze změny;_resolve_onebounds-check ({"error":"no match","display_id":n}), nejednoznačný keyword vracímatches:[{"display_id":…}];upcoming+format_upcoming(fires, id_to_display)tisknou stejné display ID jakolist; interní id zmizelo z user-facing výstupu (zůstává v audit logu a v JSON mutací). - bookmark (
bookmark.py): dvě množiny —_ordered_ids(read=False)nepřečtené (created_at DESC),read=Truepřečtené (read_at DESC);read/showřeší protilist,unreadprotihistory;_print_bookmark(row, display_id);cmd_listpočítá display ID nad celou nepřečtenou množinou (filtr--tagdrží globální čísla → očekávané mezery, jako note);cmd_addecho#1(newest-first); odstraněn deadbid. - SKILL.md obou skillů aktualizovány (EN-only): display ID = sekvenční, on-the-fly, přečíslovává se; který výpis pro který příkaz; interní id se neukazuje.
- Testy remind:
test_forecast.pyopraven na novou signaturuformat_upcoming(+ ověřuje překlad interní→display); přidánytest_display_id_renumbers_after_remove,test_id_out_of_range_reports_display_id,test_ambiguous_keyword_returns_display_ids.
Co fungovalo a proč:
- Lokálně 71 passed (
uv run --with pytest --with croniter pytest). - bookmark nemá testy → ruční ověření přes import s patchnutým
DB_PATHna temp DB: add→#1, list 1..3 newest-first,read 2→ přečíslování,show 1,--tagglobal ID,historyvlastní číslování,unread 1přes history pořadí. Vše dle návrhu. - Server = zdroj pravdy:
rsyncserver→tmp/srv/, diff všech zdrojů proti HEAD → identické (žádná autonomní divergence Dreamem) → deploy bezpečný. - Deploy
rsync(exclude__pycache__); omylem se nahrál.pytest_cache/→ smazán ze serveru. Ownernanobot:nanobotOK. Smoke test serverremind list→ display ID #1–#4 (interní id rozházená po smazáních). Skilly se čtou bez restartu.
Co zbývá: Commit kódu skillů (tracking zvlášť dle konvence — řeší uživatel). Návrh zápisu do decisions.md: load-bearing volba „--id u remind mutací = display ID, ne interní DB id".
Jak vrátit zpět: git revert <commit> skillů + rsync předchozí verze (HEAD) na server. DB schema/data beze změny.
2026-06-13 19:24 — Detach: recovery démona + úklid (code-review fixy #1,#2,#4,#5,#6,#7)
Cíl: Opravit 6 nálezů z code-review skillu /detach — dvě reálné díry v recovery démona (#1 osiřelé running/, #2 poison-loop) a čtyři úklidové (#4 duplicitní CONFIG, #5 duplicitní ensure_queue_dirs, #6 Markdown notifikace posílaná jako plain text, #7 duplicitní řazení v read-task). Plán: plans/ (schválen v plan mode). Implementaci lokálních editů dělal sonnet subagent (konvence „plány vykonává sonnet"), opus revize + deploy.
Co jsem zkusil / udělal:
- #1+#2 společný refactor
tasks-daemon.py: vytaženfinalize_task(...)(zápis Result+meta, mv do done/failed, resolve chat_id vtry, guardovaná notifikace) — sdílí hoprocess_taski novýreclaim_orphans().process_taskpřeskládán na move-first (resolve chat_id už neběží před mv ani mimo try → konec poison-loopu u ne-telegram kanálu s vadnýmallowFrom).reclaim_orphans()na startumain()označí přežitérunning/*.mdjakofailed+(INTERRUPTED…)+ best-effort notifikace. - #1d
tasks-daemon.path: přidán druhýDirectoryNotEmpty=…/tasks/running, aby reclaim nečekal na další inbox task. - #4
CONFIGimportováno ztasks_common(smazána lokální definice). #5QUEUE_DIRS+ensure_queue_dirs()dotasks_common, použito v create-task i daemonu. #6 notifikace bez zpětných apostrofů. #7find_matchesreusecompleted_files(). - Drift při porovnání se serverem: server měl
TIMEOUT_SECONDS = 20 * 60(repo/HEAD = 45), upraveno mimo repo. Per „server je zdroj pravdy" zachováno 20 min, dotaženo do repa samostatným commitem (5581411) PŘED refactorem. Potvrzeno uživatelem.
Co fungovalo a proč:
- Lokálně 57 passed (
uv run --with pytest pytest). - Server = zdroj pravdy:
rsyncserver→tmp/detach-server/, diff proti HEAD → jediný drifttasks-daemon.py(timeout). Ostatní soubory identické. - Deploy
rsync(exclude__pycache__,.pytest_cache)..pathunit je symlink z~/.config/systemd/user/do skill dir → rsync ho aktualizoval;systemctl --user daemon-reload+restart tasks-daemon.path(active). Ownernanobot:nanobotOK. - Live #1 verifikace: dummy
running/2026-06-13_11_30_00_000001-reclaim-test.md(chat_id"1"= neplatný, ať nespamuje reálný Telegram) → novýrunning/watch spustil démona → reclaim → přesun dofailed/sestatus: failed+(INTERRUPTED…). Log:RECLAIM+END status=failed+NOTIFY-FAILED HTTP 400(guard zafungoval). Test artefakt smazán zfailed/. - #2 ověřeno strukturálně (resolve uvnitř finalize po mv, v try) + reclaim test exercoval guardovanou notify cestu; live failure-injection na prod config zbytečně invazivní.
Co zbývá: Návrh zápisu do decisions.md (load-bearing, čeká na souhlas autora): (a) .path unit hlídá i running/ jako self-heal mechanismus reclaimu; (b) osiřelý task → failed (ne retry/re-queue) kvůli neopakování side-effectů agenta.
Jak vrátit zpět: git revert 999b33c (refactor) a/nebo git revert 5581411 (timeout) + rsync předchozí verze (HEAD) na server; obnovit původní .path unit (jen inbox watch) + daemon-reload + restart tasks-daemon.path. DB/tasks data beze změny.
2026-06-14 08:46 — note: explicitní registrace tagů (konec auto-create)
Cíl: /note už nesmí tagy zakládat implicitně prvním použitím. Vznik tagu = samostatný explicitní příkaz; neznámý tag při zakládání poznámky → skill se zeptá, zda ho založit.
Co jsem zkusil / udělal:
note.py: nová tabulkatags(name PRIMARY KEY, created_at);_backfill_tagsv migraci nasází distinct tagy z existujících poznámek (jen když je registr prázdný — idempotentní).cmd_add: po formát-validaci kontrola proti registru; neznámý tag → stderrUnknown tag(s): …+ exit 2, poznámka se NEUKLÁDÁ. Exit kódy: 0 ok / 1 špatný formát / 2 neznámý tag.- Nové příkazy
tag-add NAME(idempotentní, reuse_validate_tags) atag-list(řazení podle jména, bez display ID — tagy se referencují jménem; mazání se nezavádí). SKILL.md: přepsán Tag protocol (registrace povinná), Write protocol (větev na exit 2 — Ano→tag-add+re-run, Ne→re-run bez tagu), nová sekce Tag management, edge casetag addbez jména.
Co fungovalo a proč: Lokální verifikace na throwaway kopii (tmp/verify, real db naseedovaná) — backfill (arch/cli/test), known tag exit 0, unknown exit 2 + neuloženo, tag-add idempotentní, špatný formát exit 1, mixed known+unknown exit 2. Server je zdroj pravdy: před nasazením rsync serverové verze do tmp/ + diff = jen mé změny (žádný Dream edit k záchraně). Nasazeno rsync -av, vlastník nanobot:nanobot. Migrace+backfill na serveru proběhla při prvním tag-list — zaregistrováno 5 tagů (chata/devops/dt-glass/shop/test). uv na serveru je v ~/.local/bin/uv (není na non-interactive PATH).
Co zbývá: Návrh zápisu do decisions.md (čeká na souhlas autora): (a) registr tagů jako separátní tabulka + add odmítá neznámé (exit 2), vznik výhradně tag-add — load-bearing; (b) tagy se referencují jménem (bez display ID), mazání se nezavádí; (c) backfill jen při prázdném registru.
Jak vrátit zpět: git revert <tento commit kódu> + rsync -av předchozí verze skills/note/ na server. Tabulka tags v DB může zůstat (stará verze ji ignoruje); pokud vadí: sqlite3 db/note.sqlite "DROP TABLE tags;" lokálně i na serveru.
2026-06-14 10:11 — note: render-safe URL ve list (inline Markdown odkaz)
Cíl: Ve /note list se u poznámky s holými URL (#7 „DT GLASS") v chat UI nevykreslilo pořadové číslo. Diagnóza (read-only): skript i data jsou OK — note.py list tiskne N. správně a model dostává správný text. Rozbíjí to až render chatu: řádek je položka číslovaného seznamu a holé https:// URL se zároveň autolinkne do rich-link „chipu" (favicon „DG"), který se vykreslí na začátek řádku a překryje marker čísla. Ostatní řádky bez URL OK.
Co jsem udělal:
note.py: nový helper_linkify(text)— regexemhttps?://\S+obalí holé URL do inline Markdown odkazu[url](url)(inline odkaz se nerenderuje jako chip → číslo přežije, URL zůstane celé i klikací). Trailing-punctuation peel (.,;:!?)]) drží čárku/závorku za URL mimo cíl odkazu.- Použito jen v
cmd_list(řádek výpisu).cmd_showzáměrně netknut — má verbatim kontrakt; navíc není číslovaný seznam, ke kolizi nedochází.add/deletejednořádkové, bez kolize. SKILL.mdList protocol: věta, že skript URL vypisuje jako inline odkazy a model má echovat beze změny (ať nepřebaluje zpět na holé URL).
Co fungovalo a proč: Regex izolovaně lokálně (čárka i ) mimo odkaz). Server = zdroj pravdy: rsync server→tmp/ + diff note.py/SKILL.md proti HEAD = IDENTICAL (žádný Dream edit k záchraně). Nasazeno rsync -av skills/note/, vlastník nanobot:nanobot. Server note.py list po deployi: #7 má URL ve tvaru [https://…](https://…), marker 7. na začátku. Prezentační změna — DB obsah i display-ID logika beze změny.
Co zbývá: Reálné ověření renderu v nanobot UI na konkrétním modelu dělá uživatel (zda inline odkaz UI neunfurluje do chipu). Fallback při unfurlu: backtick `url` (bez klik) nebo zkrácený popisek [odkaz](url). Změna kódu skills/note/ zatím necommitnuta (čeká na pokyn).
Jak vrátit zpět: git checkout HEAD -- skills/note/scripts/note.py skills/note/SKILL.md + rsync -av skills/note/ předchozí verze na server. Bez dopadu na DB.
2026-06-14 11:33 — note: URL ve list — revert _linkify, model formátuje (bare URL na vlastní řádky)
Cíl: Dotáhnout render /note list u poznámek s URL tak, aby bylo vidět pořadové číslo i klikací odkaz. Předchozí iterace (_linkify → [url](url), viz záznam 10:11) v reálném testu selhala stejně jako holé URL.
Klíčové zjištění (přímý test s nanobot agentem): Chyba je v markdown rendereru UI, ne v textu. UI slučuje dva sousední inline odkazy [text](url) do jednoho klikacího bloku a druhý URL schová → přebije i číslo položky. Naopak holé URL se renderují správně a jsou klikací (autolink); když je každý URL na vlastním řádku / pod odrážkou, neslučují se a číslo zůstane. _linkify ([url](url)) tedy dělal přesně tu rozbíjející věc.
Co jsem udělal:
note.py: smazán helper_linkify;cmd_listvrácen na čistý výpisN. {content}{tags}(bare URL, jeden řádek).cmd_show/add/deletenetknuty, DB i display-ID logika beze změny.SKILL.mdList protocol: místo věty o „inline Markdown links" nová sekce URLs in a note s příkladem vstup→výstup — model má každý URL dát na vlastní řádek pod odrážku, holý (nikdy[text](url)), popisek spárovat s URL, číslo i[#tag]zachovat. Důvod (slučování sousedních inline odkazů) ve skillu stručně.- Architektura: formátování dělá model dle příkladu (umí spárovat popisek s URL). Deterministické formátování ve skriptu (zlom před každý URL) drženo v záloze v plánu pro případ, že glm příklad nedodrží.
Co fungovalo a proč: Server = zdroj pravdy: rsync server→tmp/note-server + diff proti repu = IDENTICAL (žádný Dream edit). ast.parse OK, _linkify pryč, re stále používáno (_TAG_RE). Nasazeno rsync -av skills/note/, vlastník nanobot:nanobot. Server note.py list: #7 „DT GLASS" má bare URL na jednom řádku, marker 7. na začátku, žádný [url](url).
Co zbývá: Reálné ověření v nanobot UI dělá uživatel — zda model (glm) příklad dodrží a render ukáže číslo + klikací URL pod odrážkami; otestovat i konzoli/Telegram. Když glm příklad nedodrží → přepnout na zálohu (deterministika ve skriptu). Změna kódu skills/note/ zatím necommitnuta (čeká na pokyn).
Jak vrátit zpět: git checkout HEAD -- skills/note/scripts/note.py skills/note/SKILL.md + rsync -av skills/note/ předchozí verze na server. Bez dopadu na DB.
2026-06-14 11:39 — note: URL ve list — fallback do skriptu (model příklad nedodržel)
Cíl: Přístup „model formátuje dle SKILL příkladu" v reálném testu selhal — glm vypsal raw řádek beze změny (jeden řádek, holé URL), UI ho celý udělalo klikací a číslo přebilo. Přepnuto na zálohu: formátování deterministicky ve skriptu.
Co jsem udělal:
note.py: helper_urls_on_own_lines(text)+ regex_LABELED_URL_RE(\s*([^\s,]+:\s*)?(https?://[^\s,]+)) — každý URL i s předsazeným „Label:" tokenem (pokud je) na vlastní odrážku-. URL zůstává holé (autolink), comma/space url neukončuje předčasně ([^\s,]+). Použito jen vcmd_list.SKILL.mdURLs in a note: model už neformátuje — echovuje verbatim (skript dodá odrážky); zákaz collapse zpět na jeden řádek a[text](url)zůstává.- Heuristika popisku: „Label:" se spáruje s URL jen když je těsně před ním (jen
\s*mezi) — vzdálený dvojtečkový token (např. „poznámka: … url") se nechytí. Cena: u prvního URL zůstane oddělovač („—") viset na konci hlavičkového řádku; přijatelné.
Co fungovalo a proč: Lokální test helperu na 4 vstupech (dvě labeled URL, bez URL, url uprostřed věty, jediný url) — výstup čistý, popisky spárované, no-URL poznámka beze změny. ast.parse OK. Nasazeno rsync -av, vlastník nanobot:nanobot. Server note.py list: #7 „DT GLASS" má hlavičku + dvě odrážky - Universal: <url> / - Amber: <url>, číslo 7. na začátku.
Co zbývá: Reálné ověření v nanobot UI dělá uživatel (číslo + klikací URL pod odrážkami; ať model echovuje verbatim a neslepí to). Otestovat i konzoli/Telegram. Změna kódu skills/note/ zatím necommitnuta (čeká na pokyn).
Jak vrátit zpět: git checkout HEAD -- skills/note/ + rsync -av skills/note/ předchozí verze na server. Bez dopadu na DB.
2026-06-11 20:02 — SOUL.md: přidány 2 behaviorální principy + zachycena autonomní divergence serveru
Cíl: Uživatel zvažoval přidat do nanobotových instrukcí blok behaviorálních pravidel (simplest approach, don't assume, surface tradeoffs, minimal code, loop-until-verified). Posoudit přínos a případně přidat.
Co jsem zkusil / udělal:
- Analýza navrženého textu proti
SOUL.md/AGENTS.md(oba se čtou do system promptu každý tah, fresh — viz knowledge.md). Závěr: ~80 % už pokryto v SOUL.md (execute-immediately ř.19, don't-assume ř.21, ask-when-unsure ř.10/23/27, verify-after-changes ř.24). Coding-specific body (minimal code, surgical diffs, clean up orphans) pro asistenta irelevantní. Reálně nové jen 2: jednoduchost a surface tradeoffs. - Stáhl serverové
SOUL.md+AGENTS.md(rsync→tmp/).diffserveru protiworkspace/SOUL.md(repo). - Server autonomně divergoval od posledního sync (
7d89c56): zahodil 3 řádky (Předpokládám znalost u uživatele…,U nevratných akcí raději počkat na potvrzení,Žádné emojis…) a přidal 1 (Čísla, limity, kvóty… ověřuj na primárním zdroji). Pravděpodobně Dream procesor. - Dle pravidla „server = zdroj pravdy": server vyhrává, 3 zahozené řádky nekřísím. Mé 2 řádky přidal do serverové kopie:
## Hodnoty: „Jednoduchost před komplexností — nejjednodušší funkční řešení; složitější vzory nenavrhuj, dokud o ně uživatel neřekne"## Pravidla provedení: „Když existuje víc cest, vynes tradeoff nahlas místo tichého výběru jedné"
rsynczpět na server, ověřen ownernanobot:nanobot+ oba řádky (ř.14, ř.26). Repoworkspace/SOUL.mddotaženo na nový serverový stav (vč. autonomních změn serveru).
Co fungovalo a proč: SOUL.md se čte fresh každý tah → bez restartu platí od příští zprávy. Porovnání se serverem odhalilo autonomní divergenci dřív, než by ji slepý přepis lokální verzí zahodil.
Co zbývá: Zvážit, zda 3 řádky zahozené serverem (zvlášť „U nevratných akcí raději počkat na potvrzení") chceme obnovit, nebo respektovat rozhodnutí Dreamu. Zatím ponecháno tak, jak je server nastavil.
Jak vrátit zpět: git revert <commit> + rsync předchozí verze na server.
2026-06-11 20:06 — SOUL.md: obnoven řádek o potvrzení nevratných akcí
Cíl: Z 3 řádků, které server autonomně zahodil (viz záznam 20:02), obnovit ten bezpečnostně relevantní.
Co jsem udělal: Posoudil všechny 3 zahozené řádky proti zbytku SOUL.md. Jen U nevratných akcí raději počkat na potvrzení byl skutečná mezera (ostatní 2 — „předpokládám znalost", „žádné emojis" — pokryto bez ozdob/terseness/USER.md, ponechány zahozené). Obnoven do ## Pravidla provedení k pravidlu o potvrzování vícekrokových úkolů, přeformulován obecněji: „U nevratných akcí (smazání, přepis, odeslání) raději počkej na potvrzení — i u jednokrokových". rsync na server, ověřen owner + řádek (ř.22), repo dotaženo.
Co fungovalo a proč: Fresh read každý tah → bez restartu.
Jak vrátit zpět: git revert <commit> + rsync předchozí verze na server.
2026-06-15 05:30 — /remind: náhodný rozvrh napříč týdnem + sync display-id featury ze serveru
Cíl: Rozšířit /remind o „N× náhodně v týdnu" (vedle stávajícího N× za den) — N náhodných odpálení rozprostřených přes celý týden.
Co jsem zkusil / udělal:
- Návrh: engine
compute_fire_times(target_date, text, cfg)zůstává per-day volaný — pro weekly režim spočítá celý týdenní plán (seed = pondělí týdne + text, ne den) a vrátí jen fires daného dne. Tím daemon i forecast nemění iterační logiku. Semantika (potvrzeno uživatelem): N různých dnů (Po–Ne), 1 čas na den, okno povinné;min(count, dostupné dny)clamp pro hraniční týdny. - Změny: sloupec
schedule_random.period('day'/'week', default 'day') + idempotentní_migrate(conn)vget_db(PRAGMA-guarded ALTER, protožeinit_dbna živou DB nesahá);_weekly_fire_timesvrandom_times.py; flag--random-times-per-week(add+edit, mutually exclusive s--random-times-per-day);perioddo INSERT/SELECT/list výpisu (× weekly/× daily). +10 testů. - Server = zdroj pravdy (klíčový krok):
rsyncserver→tmp/server-remind/,diffproti git HEAD odhalil, že server autonomně divergoval — měl feature display IDs (přečíslování reminderů na 1-based pozici vlist/upcoming,--idi chybové hlášky pracují s display ID), která nebyla v repu. Pravděpodobně Dream procesor. - Postup dle pravidla: weekly diff uložen jako patch (
git diff HEAD > /tmp/weekly.patch), pracovní strom resetován na HEAD, serverové verze 5 divergovaných souborů (SKILL.md,remind_cli.py,forecast.py,test_remind_cli.py,test_forecast.py) přeneseny do repa → commit „sync display IDs" (71 testů). Pakgit apply --3wayweekly patche — všechny.pyčistě, konflikt jen vSKILL.md(oba odstavce do stejného místa, ponechány oba) → commit weekly (80 testů). - Deploy:
rsync(exclude__pycache__/.venv/.pytest_cache), ownernanobot:nanobotOK.listna serveru → prod DB se zmigrovala (periodpřidán), stávajících 13 reminderů dál× daily.
Co fungovalo a proč: Per-day volací rozhraní enginu = daemon/forecast bez změny logiky (jen sr.period do SELECTů, protože random_cfg_from_row ho čte). Porovnání se serverem proti HEAD (ne proti pracovnímu stromu, kde už byly mé editace) odhalilo cizí divergenci — slepý rsync by display-id feature smazal. Dvoukrokový merge (sync commit → weekly na něj) zachoval obě featury, ověřeno 80 zelenými testy + kombinovaným smoke testem (display IDs #1/#2 + 2× weekly + 2 různé dny).
Co zbývá: Commit tracking souborů. Skripty se čtou per-invocation → daemon bere nový kód bez restartu.
Jak vrátit zpět: git revert 17feee6 (weekly) a git revert fcfe794 (display-id sync), pak rsync předchozí verze skillu na server. Sloupec period v prod DB lze nechat (default 'day' = původní chování) nebo ALTER TABLE schedule_random DROP COLUMN period.
2026-06-15 05:44 — /remind: vytažení data-access vrstvy do store.py
Cíl: Reakce na uživatelovu otázku, zda remind_cli.py (534 ř.) není moc dlouhý. Po re-review ne kvůli délce, ale kvůli dvěma smellům: (1) db.py deklarovaný jako storage layer neobsahoval žádné dotazy — veškeré SQL žilo v CLI; (2) transaction boilerplate (get_db/BEGIN/COMMIT/ROLLBACK/close) duplikovaný ~7× napříč cmd_*. Cíl: CLI = čistá prezentační vrstva, SQL + lifecycle do nového modulu. Veřejné API beze změny (testy musí projít).
Co jsem zkusil: Plán (plans/ přes plan mode), implementaci delegoval na sonnet agenta dle konvence. Nový scripts/store.py (240 ř.): context managery connection()/transaction() + dotazové funkce (insert_reminder, insert_schedules, fetch_reminder, schedules_for, list_active, find_active_by_id/keyword, active_display_order, soft_delete, update_text, delete_schedules, touch, set_enabled, delivered_since/today). remind_cli.py 534→400 ř., všechny cmd_* přes store.*, žádné inline SQL ani BEGIN/COMMIT. remind_send.py zatím nedotčen (volitelný krok).
Co fungovalo a proč: 80/80 testů zelených (uv run --with croniter --with pytest pytest), smoke add→list→remove OK. Past, do které jsem nespadl: working tree měl na startu necommitnutý revert display-ID → raw-DB-id v remind_cli.py (testy k němu neupravené → padaly by). Ověřením proti serveru (zdroj pravdy přes ssh) potvrzeno, že server i HEAD mají display-ID → raw-id byl stray edit, který agent správně zahodil. Bez ověření serveru bych mohl chybně „zachovat" rozbitý stav.
Co zbývá: Nasadit na server (rsync celého skillu) — server má pre-refactor display-ID verzi bez store.py; před přepisem porovnat se serverem (žádná autonomní divergence se nečeká, ověřeno). Skripty se čtou per-invocation → bez restartu. Případný decisions.md zápis (samostatný store.py vs. growth db.py) k odsouhlasení autorem.
Jak vrátit zpět: git checkout HEAD -- skills/remind/scripts/remind_cli.py && rm skills/remind/scripts/store.py (lokálně necommitnuto). Na serveru: rsync předchozí verze skillu zpět.
2026-06-15 05:50 — /remind: dokončení refaktoru (remind_send.py) + nasazení na server
Cíl: Dotáhnout volitelný krok — přesun read dotazů remind_send.py do store.py — a nasadit celý refaktor na server.
Co jsem zkusil: Do store.py přidány sender helpery (due_at, enabled_cron, enabled_random, is_fire_delivered, record_fire). remind_send.py přepsán: _due_* volají store.*, main() přes store.connection, žádné inline SQL ani get_db/init_db/conn.close. record_fire bez časové logiky (store je bezčasový) — delivered_at počítá remind_send._record_fire a předává explicitně. Nasazení: rsync server→tmp/server-remind/ + diff proti lokálu — db.py/forecast.py/random_times.py/SKILL.md shodné (žádná autonomní divergence), lišil se jen refaktor → rsync lokál→server, owner nanobot:nanobot ověřen.
Co fungovalo a proč: 80/80 testů zelených (testy volají jen main()+patch _send_telegram/_now_prague/DB_PATH, vnitřní _due_* netestují přímo → přesun bezpečný). Server smoke: list (display-ID #1/#2/#3) + import remind_send+store OK. Cron sender (* * * * * uv run .../remind_send.py) vezme nový kód při příštím běhu — per-invocation read, bez restartu. Lokálně necommitnuto (kód skillu commituji jen na vyžádání).
Jak vrátit zpět: git checkout HEAD -- skills/remind/scripts/remind_cli.py skills/remind/scripts/remind_send.py && rm skills/remind/scripts/store.py, pak rsync HEAD verze skillu na server.
2026-06-15 13:21 — /remind: explicitní instrukce pro vypsání čtecích příkazů
Cíl: Uživatel hlásil, že po nedávných úpravách /remind dva různé modely „nedokážou vrátit celou tabulku" a každý to obchází jinak. Najít příčinu (nic neměnit), pak opravit instrukce ve skillu.
Co jsem zkusil: Reprodukce CLI lokálně i na serveru (uv run … list) → exit 0, kompletní 18řádková tabulka. Vyloučeno: pád, uv/PATH (uv jen v login shellu ~/.local/bin, ale nanobot exec ho má), chybějící croniter (systémový python3 ho nemá — from croniter import croniter na ř. 22 → ImportError, ale agent jede přes uv), i refaktor store.py (3de1d6b sáhl jen na Python, SKILL.md se nedotkl). Stáhl jsem dvě webui session z poslední hodiny (workspace/sessions/): 552c62c9 (kimi27) i ee623e4b (default model) — obě spustily list úspěšně a dostaly celou tabulku, ale kimi27 odpověděl jen „Máš 18 aktivních připomínek." a druhý si postavil vlastní parafrázovanou markdown tabulku.
Co fungovalo a proč: Příčina = mezera v SKILL.md, sekci ## Behavioral contract: popisuje formát výstupu list (uvozený jako ``` ukázka → vypadá jako schéma dat), ale nikde neříká „výstup předej uživateli kompletní". Modely to čtou jako surová data a každý si je naformátuje/zkrátí po svém. Fix: nový odstavec Showing read results na začátku sekce — list/upcoming/delivered vrací user-ready text, vypsat každou položku, nesumarizovat na počet, nevynechávat řádky; hezčí formát (tabulka, parafráze cronu) povolen, pokud zůstane #display-id. Plus oprava zastaralé argparse help hlášky remind_cli.py (list „as JSON" → „as readable text"). Nasazeno rsyncem (owner nanobot:nanobot OK), CLI dál exit 0 / text, help aktualizován. Skilly se čtou per-tah bez restartu.
Co zbývá: Behaviorální ověření dělá uživatel ve webui — znovu /remind list s kimi27 i default modelem, ověřit že oba vypíšou všech 18 položek s #display-id. CLI test to neověří (chyba byla v chování modelu, ne ve skriptu).
Jak vrátit zpět: git checkout HEAD -- skills/remind/SKILL.md skills/remind/scripts/remind_cli.py, pak rsync předchozí verze skillu na server.
2026-06-15 13:28 — /remind: iterace formátu výpisu — kompaktní, model-side, jen enabled
Cíl: Po prvním kole (13:21) modely vypisovaly všech 18 položek, ale věrně relayovaly raw formát CLI → ukecané, víceřádkové, [enabled] u všech, nečitelné. Uživatel chce, ať to model sám přepíše do pěkné kompaktní podoby, vypíše jen enabled a [enabled] tag vynechá. CLI se nemění — dělá to model.
Co jsem zkusil: Přepsán odstavec Showing read results v SKILL.md (ř. 35): pro list model přepíše raw výstup do kompaktní podoby vlastní volby (řádek na připomínku, cron parafrázovaný do přirozené řeči), vypíše jen enabled (disabled přeskočí), zachová #display-id přesně jak je CLI vypsalo (mezery z přeskočených disabled OK — aby --id dál sedělo na active_display_order, které zahrnuje i disabled), bez [enabled] tagu. Obecné „nesumarizovat na počet" zůstává pro list/upcoming/delivered. Raw-formát blok pod tím ponechán (dokumentuje vstup, který model přepisuje). CLI cmd_list, store.py ani testy nedotčeny.
Co fungovalo a proč: Záměrně model-side render, ne změna Pythonu — uživatel chce „pěknou podobu podle sebe", což je prezentační volba, ne deterministický kontrakt. Klíčový constraint v instrukci: zachovat původní #display-id i při skrytí disabled, jinak by se rozbila vazba --id ↔ pořadí v CLI. Nasazeno rsyncem (owner nanobot:nanobot OK), bez restartu.
Co zbývá: Behaviorální ověření dělá uživatel ve webui — /remind list s kimi27 i default modelem: kompaktní seznam, řádek/připomínka, bez [enabled], jen enabled, se zachovanými #display-id.
Jak vrátit zpět: git checkout HEAD -- skills/remind/SKILL.md (vrátí na verzi z 13:21), pak rsync na server.
Potvrzeno (13:3x): Uživatel ve webui ověřil — výpis vypadá pěkně, kompaktní. Smyčka uzavřená.
2026-06-16 21:20 — llm-wiki: port pluginu jako skill (lokální PoC) + nasazení na server
Cíl: Portovat llm-wiki-plugin (Karpathyho „LLM Wiki" pattern) jako nanobot skill — PoC, čistě lokálně, wiki v workspace/cml/, git nad workspace/ = safety net. Plán: plans/llm-wiki-skill.md. (Širší multi-klient vize je odložená case study plans/wiki-znalostni-baze.md.)
Co jsem zkusil: Naklonoval plugin do tmp/. Zjištění proti WebFetch odhadu: SKILL.md má jen 160 ř. (ne 2200 — tělo už rozdělené, detaily v references/), 7 stdlib skriptů (PyYAML jen wiki_graph_lint+wiki_graph_extract, wiki_graph_query čte sqlite). Zkopíroval SKILL.md+scripts/+references/+assets/ (assets nutné — init_wiki.py z nich kopíruje šablony) do skills/llm-wiki/; vynechal commands/ (CC-only slash, duplikují SKILL.md), .claude-plugin/, integrations/. Skripty: PEP 723 hlavička (uv run --script, deps [] / ["pyyaml"]); default cesty Path("wiki")→Path("cml/wiki") u 4 skriptů s defaultem (robustní — agent nemusí cestu pamatovat). Docstringy skriptů nechal generické (skript je generický, nanobot-ismus patří do SKILL.md). SKILL.md: sed python …scripts/→uv run skills/llm-wiki/scripts/, .py wiki/→.py cml/wiki/; přidal „Nanobot adaptation" sekci (fixní cml/wiki+cml/raw, uv run, separate store mimo note/keep/MEMORY, Dream nesahá do cml/, lint report-only, jazyk uživatele, local PoC); krok agent-memory-integration vypnut. Bootstrap: init_wiki.py . --wiki-dir cml/wiki --raw-dir cml/raw (root . existuje, init <project-root> sám nevytváří).
Co fungovalo a proč: Lokální smoke (tmp/wiki-test): init→2 vzorové stránky→lint (detekoval broken [[wikilink]])/stats/search (BM25)/graph_lint (validace ontologie)/graph_extract (2 nodes/3 edges, 0,15 s)/graph_query (neighbors prochází mentions+typed edge) — vše přes uv run s defaultem cml/wiki bez předání cesty. Server: rsync skillu (owner nanobot:nanobot ✓), git nad workspace/ potvrzen, uv v ~/.local/bin/uv (mimo non-login PATH — pro SSH/cron plná cesta, gotcha #8). Init na serveru vytvořil cml/wiki+cml/raw (nanobot:nanobot), lint/stats/graph_extract běží pod serverovým uv (PyYAML stažen, 0,26 s). Skill se čte per-tah bez restartu. Pattern sedí na nanobot 1:1 — edit_file=chirurgické str_replace, exec=python, nezávislost na CC hooks/MCP potvrzena.
Co zbývá: (1) Behaviorální E2E přes Telegram/WebUI — NL trigger („ingest this into my wiki…", „what does my wiki say about X") dělá uživatel; ověřit reálný ingest (chunkování velkého zdroje vs 60 s exec limit, #5). (2) Fáze B: periodický wiki_lint přes HEARTBEAT (report-only). (3) Kód skillu lokálně necommitnuto (commituji jen na vyžádání).
Jak vrátit zpět: Lokálně rm -rf skills/llm-wiki. Server: ssh nanobot@nanobot.hell 'rm -rf ~/.nanobot/workspace/skills/llm-wiki ~/.nanobot/workspace/cml'.
2026-06-16 22:15 — llm-wiki: rychlý capture + dávkový compile (systémový cron → wiki_compile.py)
Cíl: PoC ingest přes WebUI trval 1:41 (agentní compile, ne skripty) → nepoužitelný interaktivně. Oddělit capture (instantní hod zdroje do cml/raw/) od compile (těžký raw→wiki) a compile spouštět na pozadí přes systémový cron. Plán: plans/llm-wiki-skill.md, sekce „Capture + dávkový compile".
Co jsem zkusil: Nový skills/llm-wiki/scripts/wiki_compile.py (vzor detach/tasks-daemon.py): shebang uv run --script + PEP 723 ["nanobot-ai"], ale from nanobot import Nanobot odložen do run_compile() — levná per-minutová pre-kontrola nesmí platit import těžkého balíku. Tok: (1) scan cml/raw/ na pending regulérní soubory (mimo _done//_hard//assets/) → prázdno = exit 0 bez LLM; (2) atomický lockfile cml/.compile.lock (PID+timestamp) přes os.open(O_CREAT|O_EXCL), stale-detekce (mrtvý PID přes os.kill(pid,0) / stáří > 30 min se přebere); (3) Nanobot.from_config() + asyncio.wait_for(bot.run(drain_goal, session_key="wiki-compile"), 25min) — drain = všechno nasbírané v jednom dávkovém běhu; (4) tiše, jen log do log/wiki_compile_cron.log. SKILL.md: nová sekce „Capture vs compile" (capture zapíše cml/raw/<slug>.md + STOP, escape hatch „compile now"; _done//_hard/ konvence) + ingest workflow zarámován jako compile/drain + fix slug-kolize (concept/entity = krátký název pojmu, source = slug zdroje — řeší původní kolizi RoPE). Crontab nanobot (PATH= už nastaven kvůli remind): * * * * * uv run …/wiki_compile.py >> …/wiki_compile_cron.log 2>&1 (forma uv run <path> jako remind, ne holý shebang — non-login/cron PATH nemá uv → env: uv: No such file or directory, gotcha #8).
Co fungovalo a proč: Lokálně (mock workspace v tmp/, --dry-run, čistý stdlib bez uv) ověřeny všechny větve: prázdný inbox→exit 0 bez logu; pending→dry-run+lock uvolněn; živý lock (reálný PID)→SKIP, lock zachován; stale lock (mrtvý PID)→reclaim. Server: deploy rsync (owner ✓), reálný compile leftover rotary-position-embedding.md proběhl na pozadí za 100 s, remaining=0, přesun do _done/, vznikly concepts/rope.md + sources/rotary-position-embedding.md + entities/paper-rope.md (slug fix drží — žádná kolize), index+log+graph (nodes/edges/sqlite/graphml) regenerovány. Empty-inbox fast path: 0,119 s, exit 0, žádné volání LLM — to je jádro efektivity minutového pollingu. Živý cron test: dropnut alibi-position.md → cron ho zvedl za ~50 s (hranice minuty), compile 107 s, přesun do _done/, concepts/alibi.md+entities/paper-alibi.md+sources/alibi-position.md, cross-link [[rope]]. Celý pipeline (capture instant → cron compile dávkově) běží end-to-end.
Co zbývá: (1) Capture přes WebUI/Telegram ověří uživatel — „ulož X" musí vrátit instant ack bez 1:41 (kód capture je instrukce v SKILL.md, ne skript → behaviorální). (2) Drobnost: agent v log.md napsal concept jako concepts/rotary-position-embedding.md, reálný soubor je concepts/rope.md (kosmetické, [[rope]] v indexu sedí). (3) Kód skillu lokálně necommitnuto (commituji jen na vyžádání). (4) Fáze B periodický lint přes HEARTBEAT — stále odloženo.
Jak vrátit zpět: Crontab: ssh nanobot@nanobot.hell 'crontab -l | grep -v wiki_compile | crontab -' (smaž i komentář řádek). Skill: git checkout HEAD -- skills/llm-wiki/SKILL.md && rm skills/llm-wiki/scripts/wiki_compile.py, pak rsync na server. Test artefakty (PoC demo obsah, lze nechat): pages pro rope/alibi v cml/wiki/ + cml/raw/_done/* + log/wiki_compile_*.log.
2026-06-16 22:45 — llm-wiki: zpřísnění capture brány (capture/compile split se nedodržoval)
Cíl: Ověřit capture chování (uživatel chtěl testovat přes WebUI). Test odhalil regresi: capture/compile split se nedodržuje.
Co jsem zkusil: Pustil capture-style goal přes bot.run(session_key="wiki-capture-test") (transport-nezávislé, jako WebUI tah): „Ulož mi do wiki: ". Agent ignoroval capture instrukci a udělal celý inline compile za 233 s — vytvořil concept/entity/source stránky, upravil rope.md, rozšířil ontologii, překompiloval graf. Tedy přesně to, čemu jsme se chtěli vyhnout (původně 1:41, teď ještě hůř). Příčina: tělo SKILL.md je převážně návod „jak udělat plný ingest", a model „ulož do wiki" čte jako „proveď ten workflow"; moje sekce „Capture vs compile" to nepřebila — byla příliš měkká a tonula v okolním textu. Druhý problém: inline compile nepřesunul zdroj do _done/, takže cron pak zvedl cml/raw/yarn.md znovu a drain agent se zacyklil (132 iterací) reconciliací už existujících stránek. Runaway proces (PID 320950, ~6 min) jsem pkill -TERM-l, smazal lock, přesunul yarn.md → _done/ (zastavilo příští cron tik). Fix SKILL.md: (1) tvrdé pravidlo nahoře v „read this first" — interaktivní požadavek = CAPTURE ONLY, inline compile je bug; (2) capture bullet přepsán na 3 kroky (zápis do cml/raw/, jednořádkový ack, STOP) + výčet zakázaných akcí (žádné čtení SCHEMA/index, žádné stránky, žádné skripty, žádný graf); (3) escape hatch zúžen („add to wiki" NENÍ escape); (4) STOP-redirect na začátku ingest workflow sekce. wiki_compile.py drain goal dostal idempotenci (existující stránky → _done/, necyklit; vždy přesunout z cml/raw/).
Co fungovalo a proč: Po redeploy znovu-test (Flash Attention): capture 26 s (jen cold-start bot.run, ne compile), odpověď „Zachyceno — zkompiluju na pozadí.", vznikl jen cml/raw/flash-attention.md, žádná wiki stránka inline — brána drží. Cron pak flash-attention zkompiloval na pozadí čistě (188 s, remaining=0, přesun do _done/, 3 stránky, bez loopu — idempotence + nový zdroj). Skill commitnut (751a11a celý skill na výslovnou žádost; 9bc52d8 zpřísnění brány).
Co zbývá: (1) Uživatel otestuje capture/query/lint sám přes WebUI (teplá session → svižnější než 26 s). (2) Drobnost — slug kolize u Flash Attention: source i concept dostaly slug flash-attention (u RoPE se přirozeně lišily rope/rotary-position-embedding); pravidlo „při kolizi suffixuj" agent neaplikoval → [[flash-attention]] dvojznačný mezi sources/+concepts/. Demo obsah, ne pipeline bug. (3) Compile time roste s velikostí wiki (100→188 s) — agent čte víc kontextu; na pozadí nevadí latencí, ale roste token cost. (4) Fáze B periodický lint přes HEARTBEAT — odloženo.
Jak vrátit zpět: git checkout HEAD~2 -- skills/llm-wiki/SKILL.md skills/llm-wiki/scripts/wiki_compile.py (před zpřísněním), rsync na server. Runaway compile už zabit, lock čistý, cml/raw/ prázdné (vše v _done/).
2026-07-01 10:40 — note: kompletní přepis na capture → compile pipeline (nasazeno)
Cíl: Překopat /note z SQLite row-store (add/list/show/delete/tagy/display ID) na osobní znalostní bázi: dumb capture z různých kanálů → periodický compile do JEDNOHO strukturovaného notes/notes.md s tematickými sekcemi; hledání = načíst celý dokument. Bez DB, čistě detach-style adresáře. Plán: plans/note-prepis.md.
Návrh (brainstorming + serverová verifikace): Architektonicky stejný vzor jako /llm-wiki, ale záměrně lehčí (jeden doc, žádný index/graph/lint). Rozhodnutí: oddělený lehký skill; tematické sekce řídí LLM; dual-mode /note <text> = okamžitě inline compile (default), /note cron <text> = odloženě přes cron; tagy zrušeny; mazání = editace dokumentu. Ověřeno na serveru před psaním kódu: web tool JE zapnutý (config tools.web: DuckDuckGo + Jina Reader fetch — TOOLS.md ho nezmiňuje!) → URL fetch přes něj, ne curl; crontab uživatele nanobot má PATH= hlavičku → bare uv run funguje; notes/ existoval prázdný; žádný drift skillu; note.sqlite = 17 aktivních notes.
Co jsem udělal:
note_capture.py— dumb atomic capture (timestamp%Y-%m-%d_%H_%M_%S_%f, staging→os.replace, frontmatter provenience, ASCII-fold slug pro diakritiku) →notes/inbox/+ řádek dolog/note.log.note_compile.py— tenký launcher (kostra zwiki_compile.py): fs pre-check bez importu nanobota, lockfile se stale-reclaim,Nanobot.from_config().run(DRAIN_GOAL)(process_direct, bez cron preamble — proto systémový crontab, ne jobs.json), timeout 15 min. DRAIN_GOAL jmenuje skill → headless agent načte SKILL.md.SKILL.mdpřepsán (EN, dual-mode, sdílený compile workflow, URL přeswebtool + paywall→hard/, search = celýnotes.md). Description zpřesněn proti kolizi skeep.- Lokální pytest (16, zelené). Starý
note.pyodstraněn z repa. Commity:a8f1997(plán),afe05d8(skill),02f9e7a(description/search fix). - Nasazeno
rsync, crontab řádek přidán (* * * * * uv run …/note_compile.py, každou minutu jako wiki/remind). Migrace: 17 aktivních notes → inbox seeding → první drain postavilnotes.md. Starýnote.sqlite→backup/note-retired/; straynotes.db/note.py/__pycache__smazány.
Co fungovalo a proč: První manuální drain 17 notes = 45 s, notes.md s čistými sekcemi (DevOps, Chata, DT Glass, Work/ZOT…); URL reálně stažena (Jina Reader) — DT Glass expandováno na produkty s cenami/rozměry, obě URL bare na vlastních řádcích, žádná fabrikace. E2e přes reálného agenta: cron auto-fire (deferred zachyt zpracován do ~80 s), /note <text> immediate (routing → note skill, capture + inline compile → nová sekce), /note search (načte notes.md, odpoví). Vše zelené.
Co zbývá / poznámky: (1) Kolize triggerů note vs keep: NL „poznamenej si …" původně spadlo do keep (obě claimovaly „ulož si"). Fix: description zpřesněn (odstraněno „ulož si", přidán kontrast „NOT durable facts → keep"). Spolehlivý je explicitní /note prefix (ten routuje správně — ověřeno). Uživatel ať v Telegramu používá /note … / /note cron …. (2) Paywall detekce je prompt-driven, live paywall netestován (logika ověřena návrhem). (3) Compile běží na default presetu; volitelně jde přepnout na rychlejší. (4) Immediate režim blokuje tah 15–100 s a roste s velikostí notes.md — proto /note cron pro dávkové sypání.
Jak vrátit zpět: Repo: git revert 02f9e7a afe05d8 (nebo git checkout afe05d8~1 -- skills/note/). Server: git-verzi note skillu rsync-ni zpět + obnov data mv ~/.nanobot/workspace/backup/note-retired/note.sqlite ~/.nanobot/workspace/db/. Crontab: crontab -l | grep -v note_compile | crontab - (smaž i komentář). notes/ (notes.md, inbox/done/hard) lze smazat — původní data jsou v backup/note-retired/note.sqlite.
2026-07-01 11:35 — note: mazání s odsouhlasením + git commit notes.md po každé změně
Cíl: Doplnit do přepsaného /note (viz 10:40) dvě chybějící provozní věci, na které uživatel upozornil: (1) mazání poznámek — s tvrdým pravidlem, že model před smazáním vždy ukáže přesný text a čeká na odsouhlasení; (2) verzování notes.md — po každé změně git add notes/ + git commit. Plán: ~/.claude/plans/u-nove-vytvoreneho-skillu-declarative-unicorn.md.
Klíčové zjištění (proč to bylo potřeba): Workspace ~/.nanobot/workspace/ je git repo, ale commituje ho jen Dream (dream: periodic memory consolidation) a ten se notes/ dotýkat nesmí. Ověřeno na serveru: celé notes/ bylo untracked (?? notes/) → notes.md se nikdy necommitovalo. Žádný existující skill vlastní commit nedělá → vzor nový.
Rozhodnutí (dotazy na uživatele): Mazání = přirozeným popisem (žádná display ID / /note list — sedí na prózový návrh). Před smazáním hard-gate: ukázat verbatim text + čekat na „ano". Commit = git add notes/ + git commit (verzuje se celé notes/, jen .compile.lock do .gitignore). Jeden committer per cesta, žádné dvojité commity: inline /note <text> a mazání → agent přes exec; cron drain → deterministicky v Pythonu v note_compile.py. Sdílený Compile workflow zůstává bez commitu (commit je odpovědnost volajícího). Prefix note: (konzistentní s dream:).
Co jsem udělal:
SKILL.md: dodescriptionpřidány delete trigger fráze (smaž poznámku o X,zapomeň X,uprav poznámku o X); do/note <text>krok commit přesexec; sekceDelete / editpřepsána na tvrdý dvoukrok (najdi → ukaž přesný text + čekej na potvrzení → surgicalstr_replace+ commit; totéž pro edit; pending zachyt = smaž soubor vinbox/); nová sekceVersioning (git).note_compile.py:commit_notes(count)— po úspěšném drainu (za drženého locku,.compile.lockje gitignored → nestaguje se)git status --porcelain notes/→ prázdné = no-op; jinakgit add notes/+git commit -m "note: cron drain (N captures)". Nikdygit add -A(Dream vlastní zbytek). Chyba commitu se jen loguje (drain už uspěl, nesmí se hlásit jako fail).- Testy
test_note_compile.py: +3 (commit_notesvytvoří commit / no-op při čistém stavu / respektuje gitignored lock), fixture dostalWORKSPACE. 19 zelených lokálně. - Server
.gitignore: přidánnotes/.compile.lockpřed deployem (živý pid-lock držený během commitu do gitu nepatří) — commitad3337f. - Deploy
rsync(--exclude __pycache__), ownernanobot:nanobot✓.
Co fungovalo a proč: E2E cron cesta ověřena na reálné infře: seed testovacího zachytu (unikátní marker ZZZTESTVERIFY…, přímo do inbox/ — ne přes capture, aby nešpinil note.log) → cron tik do ~80 s → agent zařadil do notes.md pod sekci, launcher udělal commit note: cron drain (1 captures), git status notes/ čistý (.compile.lock se nepletl → gitignore OK), cron log END … COMMIT notes/ (1 captures). Tento první commit navíc bootstrapnul celý dosud untracked notes/ strom (notes.md + 18 done souborů) — přesně očekávané. git je /usr/bin/git v2.47.3, tools.exec má prázdné allowPatterns/denyPatterns (git neblokovaný) + reálný workspace commit prošel → exec-git i launcher jsou spolehlivé. Úklid forward (bez přepisu historie): odstraněn testovací řádek + done soubor, commit chore: remove note commit verification test — marker pryč, ## Nanobot sekce nedotčená. Serverové commity: ad3337f (.gitignore), cffe3b3 (drain — bootstrap notes/), 5ed6832 (cleanup).
Co zbývá: Inline /note <text> commit přes exec a mazání s odsouhlasením nebyly spuštěny živě (LLM-driven; mazání navíc čeká na interaktivní potvrzení → nejde smysluplně headless). Vysoká jistota ze statického ověření (git dostupný, exec bez denylistu, workspace commit funguje) — dořeší reálné použití přes Telegram (/note … / „smaž poznámku o …"). Poznámka: git add notes/ verzuje i done/ → historie poroste (dle přání uživatele; případné prořezání mimo rozsah).
2026-07-01 — note/keep/bookmark: zpřesnění description o vzájemné hranice (routing)
Cíl: Dispatcher nanobota má jednoznačně poznat, kam vstup patří — note, keep a bookmark mají překrývající se doménu (poznámka vs. fakt vs. URL na přečtení). Vykonání schváleného plánu: doplnit do description všech tří skillů explicitní hranice vůči sousedům.
Co jsem zkusil: Server byl před editací ověřen jako shodný s lokálem (žádný Dream zásah), takže editace šla přímo do lokálního repa bez tahání ze serveru. Tři exact-string úpravy description:
skills/keep/SKILL.md— nově explicitně „durable personal facts, preferences, and decisions" + „NOT collecting notes, links, or articles (that is note)".skills/note/SKILL.md— poslední dva řádky přeformulovány na „reference material you file and later query" + „NOT durable personal facts, preferences, or decisions (that is keep), and NOT a bare URL you just want to read later (that is bookmark)".skills/bookmark/SKILL.md— doplněno „NOT filing or summarizing content into a knowledge base (that is note)".
Co fungovalo a proč: První pokus o editaci note/SKILL.md selhal (old_string s pouze poslední dvěma řádky nebyl v souboru nalezen), oprava — rozšířil jsem old_string o předchozí kontextový řádek, čímž byl znovu jednoznačný, a editace prošla; obsah po opravě odpovídal plánu přesně. Nasazeno rsync -av (celé adresáře, ne jen SKILL.md) na server přes IP 192.168.4.64 (hostname nanobot.hell se z tohoto stroje nerozeznává) pro všechny tři skilly. Vlastník na serveru ověřen ls -l — všechny tři SKILL.md nanobot:nanobot. Zpětné stažení serverových SKILL.md a diff proti lokálu — pro všechny tři soubory prázdný diff (plná shoda). Skilly se čtou při každém tahu bez restartu služby, takže zpřesnění je live okamžitě.
Co zbývá: Nic — nasazeno a ověřeno. Behaviorální potvrzení (že dispatcher skutečně routuje lépe na hraničních vstupech) přijde z reálného použití přes Telegram/WebUI.
Jak vrátit zpět: Repo: git checkout HEAD~1 -- skills/note/ (vrátí SKILL.md + launcher + testy). Server: rsync starší verzi skillu zpět; git.gitignore řádek notes/.compile.lock odstranit + git revert ad3337f. Feature je aditivní — necommitování notes.md je návrat k původnímu chování (Dream se notes/ stejně nedotýká).
2026-07-01 12:00 — note: bug při /note delete (hledal v SQL DB, neptal se, mazal done) → hardening SKILL.md
Cíl: Uživatel po nasazení mazání hlásil, že /note delete zot dokumentace (a) se na nic nezeptalo, (b) smazalo i soubor v done/, (c) agent se to snažil hledat v SQL DB. Najít příčinu (systematic-debugging), pak opravit.
Co jsem zjistil (z reálné stopy sessions/websocket_*.jsonl): Přesná sekvence tahu: agent paralelně read_file SKILL.md a zároveň grep "zot" v db/ → exec sqlite3 db/notes.sqlite "SELECT … FROM notes WHERE …" (tím vytvořil prázdný db/notes.sqlite) → .tables → find_files db → teprve pak grep *.md → našel v notes.md + done/…zot.md → edit_file + rm done bez potvrzení. Reasoning: „Let me check the notes database more carefully." Příčiny: (1) model má silný prior „/note delete = smaž řádek v notes DB" (chování STARÉHO SQLite note skillu) + AGENTS.md konvence „SQLite do db/*.sqlite"; (2) SKILL.md nikde nezakazoval DB/sqlite a nedokumentoval /note delete//note edit jako příkazy → model spadl do starého vzoru; (3) potvrzovací brána byla jen „prozaická", ne tvrdá STOP jako u /note cron. NENÍ to stará paměť: MEMORY.md note nezmiňuje, history.jsonl (kde stará „note=sqlite" lekce je, ř. 344) agent per-tah nečte (jen Dream) — je to prior modelu + slabý model (kimi/glm třída).
Fix (SKILL.md hardening, per rozhodnutí uživatele „done/ nechat jako interní"):
- Anti-DB callout v Architecture: „No database — ever … never search
db/, never runsqlite3, never create/open.sqlite/.db; AGENTS.md SQLite konvence se na notes nevztahuje; starší verze DB měla, je pryč." Přímo proti krokům ze stopy. - Delete/edit přepsán na
## /note delete <query> / /note edit <query>(mapování na příkaz, který uživatel reálně napsal) + tvrdá dvou-tahová STOP brána (vzor z/note cron, který prokazatelně funguje): Turn 1 = najdi jen vnotes.md(zákaz db/sqlite/inbox/done/hard) + ukaž verbatim text + STOP the turn (zakázánostr_replace/rm/git); Turn 2 = teprve po explicitním „ano" smaž + commit. Mazání se dotýká jennotes.md, NEdone/(breadcrumb je interní plumbing). - Úklid: smazán prázdný
db/notes.sqlite(0 B, artefakt chybného SQL pokusu) — jeho existence agenta utvrzovala v mylné víře (find_files db→ notes.sqlite).
Co fungovalo: Deploy rsync (jen SKILL.md), owner nanobot:nanobot ✓, oba fixy v nasazeném souboru ověřeny grepem. Skill se čte per-tah bez restartu → příští /note delete běží na opravené verzi.
Co zbývá: Behaviorální potvrzení dělá uživatel (příští /note delete musí: hledat jen v notes.md, ukázat text, zastavit a čekat, nesahat na done/). U slabšího modelu STOP brána riziko silně snižuje, ale negarantuje 100 %. Poznámka: smazaná poznámka „ZOT dokumentace" byla reálná (ne testovací) — jde obnovit z gitu (git show cffe3b3:notes/notes.md), nabídnuto uživateli.
Jak vrátit zpět: Repo git checkout HEAD~1 -- skills/note/SKILL.md, pak rsync na server. (Aditivní/behaviorální změna — revert jen oslabí brány, nerozbije data.)
2026-07-01 12:15 — note: čeština v SKILL.md + zbytečné /note varianty v description
Cíl: Uživatel v IDE otevřel skills/note/SKILL.md a upozornil na dvě věci: (1) v souboru je čeština, ačkoli EN-only pravidlo je explicitní v projektovém CLAUDE.md; (2) proč description obsahuje /note X//note cron X varianty, když nanobot ví, jak skill spustit. Požádal i o celkovou kontrolu kvality promptu.
Co jsem zjistil: Čeština byla na 4 místech (description trigger fráze + 3 místa v těle — cron potvrzení, delete/edit triggery, potvrzovací slova Turn 2). Pro otázku 2 jsem našel přímý podklad v knowledge.md („Skill description — k čemu reálně slouží"): description je čistě routing signál (kdy/proč, ne jak) a viditelný jako jeden řádek - **note** — <description>, kde jméno skillu je vždy vidět — /note X a note X tedy nic nerozlišují nad rámec samotného jména, a /note cron X je navíc detail subakce („jak"), ne routing info. Sesterský bookmark (upravovaný ve stejném committu jako note) tento princip u slash-forem už dodržuje.
Co fungovalo a proč: Oprava description (odstraněny CZ fráze + /note X//note cron X, zachována jen anglická disambiguace od keep/bookmark) + 3 CZ místa v těle převedena na anglické ekvivalenty + doplněn jazykový tag text k fencovanému stromu adresářů (chybějící dle markdown-style.md). Uživatel si mezitím sám upravil první větu description na „Capture notes, texts, ideas…" — necháno beze zásahu. Vedlejší nález: fix-markdown.ps1 hook po Edit tahu měnil emphasis z hvězdiček na podtržítka (*only*→_only_, *Versioning*→_Versioning_) — v rozporu s markdown-style.md („Never underscores"); opraveno zpět, hook to už neměnil zpátky. Nasazeno na server: hostname nanobot.hell se z tohoto stroje nerozeznává (Could not resolve hostname), IP 192.168.4.64 z CLAUDE.md funguje. Server byl před přepisem ověřen jako identický s předchozím lokálem (žádný Dream zásah) — rsync -av bezpečný. Po nasazení diff server↔lokál prázdný, owner nanobot:nanobot ✓.
Co zbývá: Stejný „zbytečná /command varianta" vzor mají i keep (/keep X), remind (/remind) a plan (/plan X) — mimo scope tohoto zadání (uživatel se ptal jen na note), zmíněno uživateli jako volitelný follow-up, nikam nezapsáno jako todo (čeká na rozhodnutí, jestli o to stojí).
Jak vrátit zpět: Repo git checkout HEAD~1 -- skills/note/SKILL.md, pak rsync na server (IP 192.168.4.64).
2026-07-01 19:33 — note: doplnění diakritiky při reformulaci + echo přesného uloženého textu
Cíl: Uživatel chtěl dvě vylepšení /note: (1) když napíše text bez diakritiky, skill má diakritiku při reformulaci doplnit; (2) u okamžitého režimu (/note <text>) má potvrzovací zpráva ukázat přesný zapsaný text, ne jen název sekce. Přes plan mode (Explore + diff proti serveru) potvrzeno, že jde jen o immediate mode — /note cron v daném tahu nic nereformuluje, takže echo tam nedává smysl.
Blocker a jeho vyřešení: SSH na nanobot.hell (pod nanobot i root, stejný klíč) nejdřív padalo na Permission denied (publickey,password) a lokálně chyběl rsync — sandbox tohoto sezení neměl potřebný klíč/nástroj. Uživatel obojí mimo tuto session doplnil („uz funguje, opravil jsem to" / „uz jsem dodal i rsync"), pak SSH i rsync fungovaly bez úprav z naší strany.
Co fungovalo a proč: V skills/note/SKILL.md (1) bod „Reformulate" v Compile workflow rozšířen o instrukci: text bez diakritiky (příklad „kdyz uz to psal bez hacku") se při reformulaci opraví na spisovnou diakritiku, už-diakritizovaný a ne-český text se nechá být; (2) krok 5 v /note <text> nahrazen tak, aby kromě sekce citoval i doslovný zapsaný text (verbatim, ne převyprávění). Před přepisem staženo rsync ze serveru a porovnáno s lokálem — server měl starší, ale skriptově identickou verzi (chyběly jen předchozí neasazené kosmetické commity popisu, žádný Dream zásah) → bezpečné nahradit celý adresář. Nasazeno rsync -av skills/note/ (celý adresář, vč. scripts/, tests/), owner nanobot:nanobot ověřen.
Co zbývá: Ruční test v chatu — /note psal jsem bez hacku a carek (diakritika v notes.md) a /note <krátký text> (potvrzení obsahuje přesný zapsaný text). Po ověření přesunout z todo.md/nezapsáno jako todo, protože šlo o přímé zadání bez čekání ve frontě.
2026-07-02 15:25 — bookmark: archivace plného textu článku jako čistý markdown
Cíl: Uživatel se zeptal, co skill bookmark udělá, když vloží URL a k tomu celý zkopírovaný článek (Ctrl+A/Ctrl+C). Zjištěno, že skill neměl žádné pravidlo — celý neočištěný článek by teoreticky přistál do description (TEXT bez limitu) a rozbil formát výpisů. Zadání: skill má rozpoznat plný článek, vyčistit ho (navigace, menu, reklamy, patičky), převést na markdown a uložit; description si dogenerovat sám, pokud ho uživatel neuvede.
Klíčové zjištění (přes plan mode, 2× Explore agent): Novou pip závislost (markdownify/trafilatura) na server nedostanu — bwrap sandbox na bare-metal nanobot.hell nemá přístup k uv cache/data adresářům (knowledge.md ř. 615–634), balíčky by při stahování padaly. Proto čištění a převod do markdownu dělá agent (LLM) na úrovni instrukcí v SKILL.md, skript zůstává stdlib-only. Precedent pro echo uloženého textu už existuje v /note (commit 739b9c8) — přejat.
Co fungovalo a proč:
bookmark.py: schéma rozšířeno o nullablecontent TEXT+ idempotentní migrace v_init_db(PRAGMA table_info→ALTER TABLE ADD COLUMN, protožeCREATE IF NOT EXISTSsloupec do existující DB nepřidá).add --content-file <path>(-= stdin, agent volá přes heredoc<<'ARTICLE'— jedno atomické volání, žádné shell-escapování). Novýcontent <id>vypíše uložený markdown. 📄 marker vlist/history/showu bookmarků s obsahem (SELECT tahácontent IS NOT NULL AS has_content, ne celý blob).- Migrace ověřena na kopii reálné serverové DB (11 řádků) před nasazením — všech 11 řádků zachováno,
contentpřidán jako NULL. ALTER ADD COLUMN je nedestruktivní. - Testy
skills/bookmark/tests/test_bookmark.py(6×, vzor znote): migrace legacy DB, add s/bez obsahu,contentpříkaz, 📄 marker. Všechny zelené. SKILL.md: nová sekce „Saving an article's full text" (detekce + čištění + heredoc + echo verbatim), pravidlo pro URL bez textu (zkusweb/Jina Reader, ale paywall/nesmysl NEUKLÁDAT,⚠ paywall/incomplete, nefabrikovat),contentpříkaz, marker v Output formatting, rozšířený Workflow.- Před zásahem dotažena serverová drift úprava frontmatteru (
NOT ... (that is note)) do repa (samostatný commit) — server byl zdroj pravdy. - Nasazeno
rsync -av skills/bookmark/, ownernanobot:nanobot✓ (omylem nahrané__pycache__uklizeny na serveru i lokálně). End-to-end na serveru přes heredoc: obsah uložen, 📄 naskočil,content 1vypsal přesný markdown vč. zachované diakritiky. Testovací řádek pak smazán z reálné DB přessqlite3(skill nemá delete příkaz) → zpět na 11 řádků.
Co zbývá: Behaviorální ověření dělá uživatel v chatu — vložit URL + článek a zkontrolovat, že agent text vyčistí, uloží a odcituje verbatim úsek; otestovat i URL-only (fetch) a paywall (neuloží obsah). Známé omezení: content <id> čte jen z unread setu (jako show) — obsah už přečtených bookmarků z history zatím nedostupný.
Jak vrátit zpět: Repo git checkout HEAD~1 -- skills/bookmark/ + git rm skills/bookmark/tests/, pak rsync na server. Migrace DB je aditivní a nedestruktivní — content sloupec může na serveru zůstat (ignoruje ho starší verze skriptu). Data v content by revert skriptu zneviditelnil, ne smazal.
2026-07-02 17:00 — bookmark: soft delete, HTML→markdown utilita (trafilatura), fetch-fail výzva
Cíl: Tři navazující požadavky na bookmark (přes plan mode, opakované AskUserQuestion): (A) když se nepodaří stáhnout obsah URL, agent si má o vložení článku sám říct; (B) delete pro mazání článků (omylem vložený duplikát); (C) když je vložený obsah HTML, převést ho na markdown Python utilitou, ať to nedělá drahý model.
Klíčová oprava předpokladu: U hlavní feature (viz záznam 15:25) jsem tvrdil, že bwrap sandbox nepustí pip závislost. Ověřeno naživo — na nanobot.hell to neplatí: produkční exec běží "sandbox": "" (bez bwrap), uv + PyPI fungují, uv run --with trafilatura stáhne a naimportuje (v2.1.0). Jiné skilly reálně jedou se závislostmi (remind→croniter, llm-wiki→pyyaml). Bwrap limit z knowledge.md ř. 615–634 platí jen pro nasazení se zapnutým sandboxem. → doplněna korekce do knowledge.md.
Rozhodnutí uživatele (AskUserQuestion): delete = jen soft delete (flag deleted_at, skryje z výpisů, řádek zůstává v DB; bez restore/deleted příkazů) + potvrzení před smazáním (řeší agent). HTML převod: uživatel navrhl markitdown; ověřeno ze zdrojáku, že markitdown pro HTML = markdownify (převede celé <body>, neodstraní boilerplate) + těžší (magika/onnxruntime) → dominovaná. Zvolena trafilatura (odstraní boilerplate A vrátí markdown v jednom kroku). Plain text: model čistí jen při zjevném balastu. Detekce HTML: rozhodne agent.
Co fungovalo a proč:
bookmark.py:deleted_at TEXT+ migrace (idempotentní ALTER, vzor jakocontent);AND deleted_at IS NULLdo obou větví_ordered_ids+ přímých SELECTů vlist/history(žádný „subset" refaktor — unread/read je jen negaceread_at, na připomínku uživatele); novýcmd_delete(soft, resolve z unread setu). Potvrzení je záměrně na úrovni agenta (SKILL.md), ne skriptu.html_to_markdown.py: nový PEP 723 helper (dependencies=["trafilatura"], shebanguv run --script), stdin HTML → stdout markdown; prázdný vstup exit 1, nic neextrahováno exit 2. Agent ho pipe-uje dobookmark.py add … --content-file -→ převedený markdown teče shellem, neprochází kontextem modelu (levné). Když trafilatura nic nevrátí → prázdný content → NULL → fallback na čištění modelem.SKILL.md: rozcestník HTML (pipe helper) vs plain text (uložit rovnou, čistit jen při balastu); delete + potvrzovací flow + display-ID caveat; při paywallu/nezdaru stažení agent požádá uživatele o vložení článku místo tichého fallbacku.- Testy (10× zelené): soft delete (skryje, řádek zůstává), delete out-of-range, migrace
deleted_at,html_to_markdownextrakce (boilerplate pryč,#/##nadpisy) + prázdný vstup. Lokálně ověřeno i na realistickém HTML (nav/reklama/patka odstraněny) a exact pipe konstrukt. - Migrace
deleted_atověřena na kopii reálné serverové DB (16 řádků zachováno, NULL). Nasazenorsync -av --exclude=__pycache__, ownernanobot:nanobot. E2E na serveru: HTML pipe →content 1vrátí čistý markdown;delete 1→ soft (deleted_atset, řádek v DB). Testovací řádek pak hard-smazán přessqlite3→ zpět 16.
Co zbývá: Behaviorální ověření uživatelem v chatu (agent má u HTML pipe-ovat helper, u plain textu čistit jen při balastu, u paywallu požádat o vložení, před delete potvrdit). Známá omezení: delete/content jen z unread setu (list), ne z history; žádný restore (soft-smazané jen ručně přes DB); trafilatura potřebuje realistickou stránku — na drobném snippetu boilerplate nepozná (fallback na model to řeší).
Jak vrátit zpět: Repo git revert skill committu + rsync na server. Migrace (deleted_at) aditivní/nedestruktivní — sloupec může na serveru zůstat. Soft-smazané řádky mají deleted_at set; revert skriptu je zase zviditelní (data se neztratí).
2026-07-06 — remind: deterministická pojistka proti duplicitnímu textu + steering
Cíl: /remind občas místo jednoho reminderu s víc časy založil víc samostatných reminderů se stejným textem. Nanobot (Dream) na to reagoval přidáním jednoho příkladu do serverového SKILL.md. Uživatel se ptal, jestli to stačí — po rozboru padlo rozhodnutí přidat i tvrdou pojistku: jeden text nikdy nepotřebuje víc záznamů, takže add/edit mají duplicitu odmítat chybou.
Důkaz příčiny (ne bug v kódu): Serverový log/reminder.log — „UFO burger" id=46 (18:00) + id=47 (07:00) 7 s po sobě (split jednoho zadání); dřív totéž „note #work" id=41/42/43. CLI mechanismus byl přitom vždy správný: --at/--cron jsou action="append", insert_schedules vloží všechny naráz → add --at X --at Y = jeden reminder, dva časy. Příčina = chování agenta, který zadání rozdělil do víc add volání.
Co jsem udělal:
store.py:find_active_by_exact_text(conn, text, exclude_id=None)— porovnání Python-sidecasefold()na trimnutém textu (ne SQLlower(), to v SQLite nefolduje unicode diakritiku → „Čaj"/„čaj" by prošlo).remind_cli.py: helper_reject_duplicate_text(vrací{"error":"duplicate text", display_id, hint}na stderr, exit 1). Zapojen vcmd_add(před transakcí) acmd_edit(při změně textu,exclude_id=rid→ přejmenování na vlastní text projde). Hint navádí na recovery po splitu:edit --id N --replace-schedulesse všemi časy.SKILL.md: do mapovací tabulky přidán cron multi-time řádek (--cron … --cron …), zachován serverový--at/--atpříklad (dotažen do repa); do Behavioral contract explicitní pravidlo „jeden text = jeden reminder".- Testy: 3 nové (
test_add_rejects_duplicate_text,test_add_duplicate_case_and_diacritics_insensitive,test_edit_text_collision_rejected); 2 existující dup testy (test_remove_by_id_disambiguates_duplicates,test_ambiguous_keyword_returns_display_ids) staví duplicitu přes nový helper_seed_duplicate(přímýstore.insert_reminder, obchází guard) — disambiguace pre-existujících dupů zůstává validní.
Co fungovalo a proč: 83 testů zelených. Lokální smoke (temp REMIND_DB): add#1 OK, add#2 stejný text → odmítnut (exit 1, display_id, hint), správná cesta add --at … --at … → jeden reminder se dvěma at. Před deployem ověřeno, že serverové skripty == git HEAD (žádná autonomní divergence Dreamem), SKILL.md se lišil jen o ten jeden --at řádek (dotažen). Nasazeno rsync -av skills/remind/, owner nanobot:nanobot ✓. Server == lokál u všech změněných souborů. Na serveru jsem mutace nespouštěl (log_operation ignoruje REMIND_DB → zapsalo by do reálného reminder.log).
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ý.