runtime
This commit is contained in:
@@ -1,5 +1,134 @@
|
||||
# History
|
||||
|
||||
## 2026-09-11 — AGENTS.md: pravidlo o exec guardu globálně + srovnání skillu `note`
|
||||
|
||||
**Cíl:** Navázat na opravu skillu `project` (záznam níže) a zapsat pravidlo o exec
|
||||
guardu na místo, kde pomůže všem skillům, ne jen tomu, kde se zrovna projevilo.
|
||||
|
||||
**Co jsem zkusil / co jsem našel:**
|
||||
|
||||
Nejdřív jsem zjišťoval, který soubor nanobot vůbec načítá do system promptu.
|
||||
`build_system_prompt()` (`agent/context.py:70–121`) skládá prompt z
|
||||
`BOOTSTRAP_FILES = ["AGENTS.md", "SOUL.md", "USER.md"]`. Z nich je **`AGENTS.md` jediný
|
||||
user-ownovaný** — `SOUL.md`, `USER.md` i `memory/MEMORY.md` přepisuje Dream, bundled
|
||||
`templates/` přepíše upgrade balíčku.
|
||||
|
||||
Při čtení `AGENTS.md` vyšlo najevo, že **tentýž bug už tam byl zdokumentovaný dvakrát
|
||||
izolovaně**: sekce `## Git commit timestamps` říká, že `date '+%Y-%m-%d %H:%M:%S'` guard
|
||||
blokuje (ověřeno — token `H:%M:%S`, `H` po `%`), a sekce `## exec Tool` řešila chybějící
|
||||
workspace path. Ani jednou se nepojmenovala společná příčina. Třetí výskyt byl živý:
|
||||
`note_capture.py --text "Cíl: koupit mléko"` → blok (token `l:`), přičemž
|
||||
`note/SKILL.md` explicitně nařizuje „Pass the input **as-is**". Poznámka uživatele
|
||||
začínající `Cíl:` tedy tiše selhávala — a capture bere vstup doslova, takže to nebyl
|
||||
okrajový případ, ale běžná česká formulace.
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
- **`workspace/AGENTS.md`** — nejdřív dotažena autoritativní serverová verze do repa
|
||||
samostatným commitem (lokální kopie byla zastaralá a server je zdroj pravdy), teprve
|
||||
pak přidáno pravidlo do sekce `## exec Tool`: text nikdy do command stringu, vždy
|
||||
`write_file` do `tmp/` + předání cesty. Sekce `## Git commit timestamps` ponechána —
|
||||
jmenuje konkrétní skript a je jen instancí obecného pravidla.
|
||||
- **`skills/note`** — `--text` nahrazen za `--file <path>` (fail fast na chybějící
|
||||
soubor), obě volací místa v SKILL.md převedena na dvoukrok, přidána sekce *Never pass
|
||||
the text on the command line*. Testy 19 → 22.
|
||||
|
||||
Ověřeno na serveru proti **nainstalovanému** guardu:
|
||||
|
||||
```text
|
||||
PROJDE NOVY --file -> []
|
||||
BLOKOVAN STARY --text -> ['l:', 'r:']
|
||||
```
|
||||
|
||||
End-to-end: `write_file` textu `Cíl: koupit mléko. Závěr: zítra.` + `--file` → soubor
|
||||
v `notes/inbox/` s korektním frontmatterem, exit 0. Inbox pak uklizen, **`log/note.log`
|
||||
ne** — je to append-only audit log a mazat ho by porušilo přesně to pravidlo, které
|
||||
tahle práce kodifikuje. Zůstal tam jeden testovací CAPTURE řádek z 15:09.
|
||||
|
||||
Ověřeno i to, že se `AGENTS.md` do promptu opravdu dostává (liší se od
|
||||
`templates/AGENTS.md`; při shodě by ho `context.py:179–182` tiše vynechalo).
|
||||
|
||||
**Co zbývá:**
|
||||
|
||||
- `bookmark` (český článek v heredocu) a `remind` (`edit --text`) mají pořád stejnou
|
||||
expozici — vědomě mimo scope, vedeno v `knowledge.md`.
|
||||
- `AGENTS.md` se bere z `project_root` aktuálního tahu (`context.py:163`), takže
|
||||
v session scoped do `tmp/<x>` se root verze nenačte. Pro chat workflow to nevadí;
|
||||
univerzální varianta by byl skill s `always: true`.
|
||||
- Regex v nanobotu zůstává rozbitý; tohle je workaround, ne fix.
|
||||
|
||||
**Jak to vrátit zpět:** `git revert` příslušných commitů + rsync `workspace/AGENTS.md`
|
||||
a `skills/note/` na server. Nic se nemazalo kromě vlastních testovacích artefaktů.
|
||||
|
||||
## 2026-09-11 — project: zápis do memory.md padal na exec guardu, převeden na --file
|
||||
|
||||
**Cíl:** Agent na serveru nedokázal zapsat záznam do `projects/life/memory.md` —
|
||||
tři pokusy po sobě spadly na `Command blocked by safety guard (path outside
|
||||
working dir)`. Zjistit proč a opravit skill, aby se to neopakovalo.
|
||||
|
||||
**Co jsem zkusil / co jsem našel:**
|
||||
|
||||
Stažena session `002f2196` z `~/.nanobot/webui/`. Pořadí selhání: quoted heredoc
|
||||
→ `printf | pipe` → `--text`, všechny se stejnou chybou; uspěl až `write_file` do
|
||||
`tmp/` + `log life < tmp/soubor`. Agent si to ve vlastním shrnutí vyložil jako
|
||||
„nedeterministický blok na datu a tečkách" — **to je mylné**.
|
||||
|
||||
Skutečná příčina, ověřená spuštěním nainstalovaného guardu
|
||||
(`/home/nanobot/.local/share/uv/tools/nanobot-ai/…/nanobot/agent/tools/shell.py`)
|
||||
na reálných příkazech: `ExecTool._extract_absolute_paths()` hledá windowsové cesty
|
||||
regexem s **ASCII-only negativním lookbehindem** `(?<![A-Za-z])[A-Za-z]:…`. Znak
|
||||
s diakritikou před ASCII písmenem lookbehind neutne, takže `Cíl:` → token `l:`,
|
||||
`Závěr:`/`směr:` → `r:`. `Path("r:").resolve()` se rozvine vůči cwd démona
|
||||
(`/home/nanobot`, ne vůči workdiru příkazu) → `/home/nanobot/r:` → mimo workspace
|
||||
→ blok. Guard navíc jede nad **raw command stringem bez shell parseru**, takže
|
||||
nerozliší argument od obsahu heredocu — proto padly všechny tři varianty stejně.
|
||||
Blokovaný text obsahoval `Diagnóza/směr:`; `artifacts/fyzio-cviky-kycele.md` ani
|
||||
lomítko samo o sobě vinné nebyly.
|
||||
|
||||
Kontrolní vzorek proti nainstalovanému guardu (`--file` vs. staré varianty):
|
||||
|
||||
```text
|
||||
PROJDE NOVY --file -> []
|
||||
BLOKOVAN STARY heredoc -> ['l:', 'r:', 'r:']
|
||||
BLOKOVAN STARY --text -> ['l:', 'r:', 'r:']
|
||||
BLOKOVAN STARY printf|pipe -> ['l:', 'r:', 'r:']
|
||||
```
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
Oprava není „jiný způsob citace", ale **text se nikdy nesmí dostat do command
|
||||
stringu**. Konkrétně:
|
||||
|
||||
- `project_cli.py`: `--text` odstraněn, přidán `--file <path>` (fail fast na
|
||||
neexistující soubor). Stdin zůstal jako fallback. Flag místo shell redirectu
|
||||
proto, že celý failure mode byl model improvizující shell syntax.
|
||||
- `SKILL.md`: sekce „Writing to memory.md" přepsána na dvoukrok `write_file` do
|
||||
`tmp/` + `log <slug> --file …`, s vysvětlením **proč**, ať to nikdo
|
||||
neoptimalizuje zpátky na heredoc.
|
||||
- `SKILL.md` Rules: zákaz testovacích zápisů do ostrého projektu — agent si při
|
||||
diagnostice zanesl do append-only `memory.md` dvě test položky a pak je ručně
|
||||
odstranil, čímž porušil „never rewrite history". Od toho je `new <scratch-slug>`.
|
||||
- Testy: 15 → 20. Sekce `log` převedena na fixture `entry_file`, takže testuje
|
||||
reálnou cestu přes soubor. Přibyl regresní test s `Cíl:`/`Závěr:`, test na
|
||||
chybějící soubor a pojistka, že `--text` v argparse skončí `SystemExit 2`.
|
||||
|
||||
Před nasazením ověřena shoda serveru s repem (`rsync` + `diff -r` → lišil se jen
|
||||
`__pycache__`). Nasazeno rsyncem, vlastník `nanobot:nanobot` ověřen, restart
|
||||
služby není potřeba. End-to-end test proti scratch projektu `guard-test` (ne proti
|
||||
`life`) s textem `Cíl: … Diagnóza/směr: … Závěr: …` prošel, exit 0; scratch
|
||||
projekt i `tmp/guard-test.md` pak smazány.
|
||||
|
||||
**Co zbývá:**
|
||||
|
||||
Guard v nanobotu je pořád rozbitý a **stejnou expozici mají další skilly** —
|
||||
`note` (`--text "<raw input>"` se syrovým českým vstupem uživatele, nejrizikovější),
|
||||
`bookmark` (český článek v heredocu) a `remind` (`edit --text`). Vědomě mimo scope
|
||||
této opravy, zapsáno do `knowledge.md` jako známý dluh. Plošný fix by znamenal
|
||||
unicode-aware lookbehind v upstreamu (resp. ve forku `nanobot-custom-commands`).
|
||||
|
||||
**Jak to vrátit zpět:** `git revert` commitu se skillem + rsync `skills/project/`
|
||||
na server. Žádná data se nemazala kromě scratch projektu `guard-test`.
|
||||
|
||||
## 2026-09-02 — reflect: noční běh padal na timeout a zahazoval hotové dávky
|
||||
|
||||
**Cíl:** Noční cron (`30 3 * * *`) neodběhl — ve 4:00 přišlo na Telegram
|
||||
@@ -3455,3 +3584,278 @@ návrh do `decisions.md`) se nemění.
|
||||
**Jak vrátit zpět:** `git revert 3adc2ef`, pak na serveru rsync skillu a
|
||||
`wiki_sync.py --full` (titulky se vrátí na NULL). Index se tím nerozbije — `title` je
|
||||
jen sloupec v katalogu, retrieval na něm nestojí.
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-15 06:20 — Ollama usage poller: revize serverového plánu + nasazení sběru
|
||||
|
||||
**Cíl:** Uživatel chtěl dotáhnout serverový plán `plans/ollama-usage-poller.md`
|
||||
(napsal si ho nanobot sám 2026-09-14) do repa a probrat, jak ho realizovat a kde
|
||||
má slabiny. Záměr plánu: minutový cron → SQLite, aby šlo zpětně zjistit, kolik
|
||||
která session stála.
|
||||
|
||||
**Co jsem zkusil:** Nejdřív ověření předpokladů plánu proti reálnému serveru
|
||||
(ne proti tomu, co plán tvrdí). Nálezy, kvůli kterým se návrh měnil:
|
||||
|
||||
- **`memory/history.jsonl` nemá tokeny.** Plán na něm stavěl budoucí atribuci
|
||||
(„per-request timestamps, token counts, session ids"). Reálně obsahuje jen
|
||||
`{cursor, timestamp, content}` (+ `session_key` u 59 ze 430 řádků) — je to
|
||||
destilát paměti z Dreamu. `sessions/*.jsonl` má `role/content/timestamp/
|
||||
tool_calls/reasoning_content/latency_ms`, **taky bez tokenů a bez modelu**.
|
||||
Jediný zdroj tokenů je `LLM usage:` v journalu, což je DEBUG a `-v` už v
|
||||
`ExecStart` **není** (poslední takový řádek je z 2026-05-27) — `knowledge.md`
|
||||
tvrdila opak, opraveno.
|
||||
- **`limits.*.usage` má rozlišení 0,1 %** (API vrací 3 desetinná místa).
|
||||
Plánovaný report `Δsession %, Δweekly %` per dvojice vzorků sliboval přesnost,
|
||||
kterou data nemají → osa reportu přepnuta na `request_count` per model.
|
||||
- **`models` je pole `[{name, request_count}]`**, ne slovník jak tvrdil komentář
|
||||
ve schématu → kanonizace na setříděný dict, jinak by se zapisovalo každou minutu.
|
||||
- **Umístění `workspace/scripts/`** odporovalo vzoru — všech 6 existujících cron
|
||||
entry míří do `skills/<name>/scripts/` → přesunuto do `skills/usage/scripts/`,
|
||||
čímž odpadl i `sys.path` hack kolem reuse `load_api_key`.
|
||||
- **Write-on-change nerozliší klid od výpadku** → doplněna tabulka
|
||||
`meta(last_ts, last_status)`, přepisovaná při každém běhu včetně chybového.
|
||||
- Ukládá se i `session.models` (plán si nechával jen `weekly`).
|
||||
|
||||
**Co fungovalo a proč:** Skill `usage` dotažen do repa (dosud tam vůbec nebyl),
|
||||
přidány `ollama_usage_poll.py` + `ollama_usage_report.py` (stdlib only, EN),
|
||||
sekce „Continuous sampling" do `SKILL.md`, rsync na server (`nanobot:nanobot`),
|
||||
cron entry `* * * * *`. Ověřeno:
|
||||
|
||||
1. Dva běhy po sobě → `samples` má 1 řádek, `meta.last_ts` se posunulo oběma
|
||||
(04:19:46 → 04:19:53). Write-on-change funguje.
|
||||
2. `OLLAMA_API_KEY=bogus` → exit 0, `meta.last_status = 'http_401'`,
|
||||
žádný nový řádek. Cron se tím nezanáší.
|
||||
3. Logika reportu ověřena na syntetické DB ve scratchpadu: hranice session okna
|
||||
(pokles `session_usage` i součtu `session_models`), mezera v datech,
|
||||
per-model delty, souhrn.
|
||||
4. Cron potvrzen (`meta.last_ts` se posouvá bez ručního spuštění,
|
||||
`log/ollama_usage_cron.log` prázdný).
|
||||
|
||||
**Co zbývá:** Atribuce na sessions — přesunuto do `todo.md`, až bude pár dní dat.
|
||||
Vedlejší přínos, který plán nepojmenoval: minutová řada ověří/vyvrátí domněnku
|
||||
ve `skills/usage/SKILL.md`, že session okno je kalendářní hodina
|
||||
(`until_next_full_hour` je odhad, ne údaj z API).
|
||||
|
||||
**Jak vrátit zpět:** `crontab -e` a smazat entry `ollama-usage`;
|
||||
`rm ~/.nanobot/workspace/db/ollama_usage.sqlite`;
|
||||
`rm ~/.nanobot/workspace/skills/usage/scripts/ollama_usage_{poll,report}.py`
|
||||
a rsync `SKILL.md` z gitu před commitem. Lokálně `git revert`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-15 10:05 — Session okno: fixní 5h blok, fáze kalibrovaná z DB
|
||||
|
||||
**Cíl:** Skill `/usage` hlásil „Session: … resets in 2 minutes", zatímco web
|
||||
ukazoval 4 hodiny. Zjistit proč a dát tomu odhad, který se dá věřit.
|
||||
|
||||
**Co jsem zkusil:**
|
||||
|
||||
1. Porovnání serveru s repem — server běžel se starou verzí z 14. 9.
|
||||
(`until_next_full_hour()`). **Oprava z předchozí session se nikdy nenasadila**
|
||||
a ležela necommitnutá v repu. To je celé vysvětlení „2 minut" (běželo 06:58 UTC).
|
||||
2. Data z `db/ollama_usage.sqlite`: session spadla z `0.077`/21 req na `0.0`/`{}`
|
||||
mezi vzorky 04:28:01 a 06:00:01 → **fixní blok, ne rolling okno** (rolling by
|
||||
se rozpadal postupně).
|
||||
3. Kalibrace proti webu (uživatel hlásil, co vidí): ~06:58 „4 hodiny",
|
||||
~07:05 „3 hodiny", session **2,3 %** — procento přesně sedí s API, takže
|
||||
dashboard a `/api/usage` čtou totéž.
|
||||
4. Délka okna z primárního zdroje — [transparent-pricing](https://ollama.com/blog/transparent-pricing):
|
||||
nové plány zrušily „5-hour or weekly limits", tenhle klíč je má → **5 h**.
|
||||
5. Původní návrh přidával do polleru heartbeat, aby šlo mezeru v `samples`
|
||||
odlišit od výpadku. **Uživatel ho zamítl** — DB má zůstat čistě změnová.
|
||||
Ukázalo se, že je zbytečný (viz níže).
|
||||
|
||||
**Co fungovalo a proč:** Mřížka je periodická, takže každý pozorovaný reset
|
||||
omezuje **tutéž fázi**. Jedno pozorování ohraničí hranici intervalem
|
||||
`(poslední vzorek před, první vzorek po]`; průnik těch intervalů **modulo 300
|
||||
minut** fázi dopočítá. Dvě vlastnosti, proč to heartbeat nepotřebuje:
|
||||
|
||||
- Ohraničení platí, i když poller neběžel — **výpadek interval jen rozšíří**,
|
||||
nikdy nezpůsobí špatný závěr. Není tedy co dokazovat.
|
||||
- Když je délka bloku špatně, průnik se **vyprázdní** místo tiché chyby.
|
||||
|
||||
Dokud je kandidátů víc než `MAX_SPREAD`, reset se nevypisuje — dnešní data dávají
|
||||
93 z 300, takže skill zatím hlásí holé `Session: 2.4 %`. To je správný výstup.
|
||||
|
||||
Nález při psaní výpisu mřížky: **5 h nedělí 24 h**, takže „mřížka 00/05/10/15/20
|
||||
UTC" nemůže být stálá — buď startuje denně (s kratším posledním blokem), nebo
|
||||
plave. Obě hypotézy jsou zatím konzistentní s daty a průnik je rozliší; report
|
||||
proto tiskne jen **příští hranici**, ne seznam denních.
|
||||
|
||||
Časy ve výstupu jsou v lokální zóně serveru (`.astimezone()`, nic
|
||||
nehardcodováno), ale výpočet zůstává v UTC — v lokálním čase by ho přechod na
|
||||
zimní čas 25. 10. posunul o hodinu a zahodil kalibraci.
|
||||
|
||||
Ověřeno: 19 kontrol na syntetických datech (zúžení průniku, prázdný průnik,
|
||||
bracket ≥ 300 min, drift přes den, rozestup přes DST), proti reálné DB
|
||||
(93 kandidátů → `None`), formátování pod `TZ=Europe/Prague`/`UTC`/`America/New_York`,
|
||||
a po nasazení přímo na serveru včetně běhu polleru. Uzavírá položku
|
||||
z `todo.md` o ověření domněnky o kalendářní hodině.
|
||||
|
||||
**Co zbývá:** Rozhodující pozorování — příští hranice ve **12:00 nebo 13:00
|
||||
CEST**. Podle toho, jak blízko ní bude poslední aktivita, se fáze zúží a skill
|
||||
začne reset hlásit sám. Pak porovnat s webem.
|
||||
|
||||
**Jak vrátit zpět:** `git revert ff9ac3e` + rsync `skills/usage/` na server.
|
||||
|
||||
## 2026-09-15 10:15 — Session okno se kotví requestem, ne mřížkou (oprava modelu)
|
||||
|
||||
**Cíl:** Uživatel namítl dvě věci k předchozímu záznamu: (a) mlčení místo odhadu
|
||||
mu nepomáhá, „lepší něco než nic"; (b) **hranice přece nemusí být pevná — nemůže
|
||||
se začít počítat od prvního dotazu po pauze?**
|
||||
|
||||
**Co jsem zkusil:** Bod (b) je testovatelný, tak jsem šel do dat dřív, než jsem
|
||||
dodělal (a). Z `sessions/*.jsonl` jsem vytáhl timestampy assistant zpráv a
|
||||
poskládal bloky aktivity oddělené pauzami. Rekonstrukce (UTC):
|
||||
|
||||
| Okno | Start | Konec |
|
||||
|---|---|---|
|
||||
| W2 | 00:00 (první request po vypršení W1) | 05:00 |
|
||||
| W3 | 06:00 (první request po vypršení W2) | 11:00 |
|
||||
|
||||
**Co fungovalo a proč:** Rozhodl **chybějící zápis v 05:00**. Kdyby existovala
|
||||
pevná mřížka, usage by se v 05:00 vynulovala a poller (běží každou minutu) by
|
||||
změnu zapsal. Místo toho se nezměnilo nic až do 06:00, kdy přišel první request
|
||||
po 93minutové pauze. Vzorek `0.0`/`{}` v 06:00:01 tedy **není okamžik resetu, ale
|
||||
okamžik startu nového okna**. Pevná mřížka by navíc vyžadovala, aby ten request
|
||||
padl na hranici náhodou (~1:93).
|
||||
|
||||
Počty requestů to nerozhodly (23 v 00:00–05:00 vs 19 v 01:00–06:00 proti
|
||||
hlášeným 21) — assistant zprávy nejsou 1:1 s LLM requesty.
|
||||
|
||||
Model přepsán na **poslední pozorovaný přechod + 5 h**. Vedlejší efekt: je to
|
||||
výrazně jednodušší než průnik kandidátních fází modulo 300 min, který jsem
|
||||
nasadil o hodinu dřív, a **hlásí reset okamžitě** — čímž vyřešil i bod (a).
|
||||
Server teď říká `Session: 2.5 %, resets 13:00 CEST (in 2 h 47 min)`.
|
||||
|
||||
Za konec okna se nic neextrapoluje: po vypršení výstup řekne, že další okno
|
||||
začne dalším requestem. Report dostal `Rollover gap:` řádky jako průběžný test
|
||||
modelu — rozestup delší než blok potvrzuje kotvení requestem, rozestup přesně
|
||||
roven bloku přes dlouhou nečinnost by ukazoval zpět na pevnou mřížku.
|
||||
|
||||
Ověřeno 15 kontrolami na syntetické DB (kotva, víc přechodů, chybějící DB,
|
||||
vypršelé okno, okno přes přechod na zimní čas, formátování v UTC i CEST),
|
||||
proti reálné DB a po nasazení na serveru.
|
||||
|
||||
**Poučení:** Předchozí záznam (09:xx) tvrdil „fixní blok" a stavěl na tom celou
|
||||
kalibrační mašinerii. Hypotézu „start prvním requestem" jsem měl v úvahách na
|
||||
začátku, ale zahodil ji bez testu — přitom šla ověřit ze `sessions/*.jsonl`,
|
||||
které jsem měl celou dobu k dispozici. Past, která to držela naživu: crony
|
||||
běží na celé hodiny, takže hranice *vypadaly* jako mřížka.
|
||||
|
||||
**Jak vrátit zpět:** `git revert 9f39d29` + rsync `skills/usage/` na server.
|
||||
Předchozí (mřížkový) model je `ff9ac3e`.
|
||||
|
||||
## 2026-09-15 10:40 — Review a zeštíhlení skillu `usage`
|
||||
|
||||
**Cíl:** Projít `skills/usage/SKILL.md` — jestli splňuje náležitosti skillu a jestli
|
||||
v něm nejsou zbytečnosti. Plus odpovědět, k čemu je v DB tabulka `meta`.
|
||||
|
||||
**Co jsem zkusil:** Přečetl SKILL.md, všechny tři skripty, `knowledge.md`, porovnal
|
||||
serverovou verzi s lokální (server == HEAD, žádný drift). Review podle
|
||||
`core:skill-authoring`.
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
- **Zbytečnosti.** Sekce „Reset times" byla ze ~40 řádků z větší části forenzní důkaz
|
||||
— *jak* jsme model session okna naměřili. 1:1 duplikát `knowledge.md` („Ollama session
|
||||
okno"), history 2026-09-15 10:15 a docstringů `window_rollovers()` / `print_window()`.
|
||||
V SKILL.md se platí kontextem při každém načtení a agentovi neříká, co má dělat —
|
||||
vyhozeno, důkazy zůstávají v tracking souborech a v kódu. Pravidlo „žádné modely na
|
||||
Session/Weekly řádcích" bylo řečené třikrát, falzifikační odstavec o `Rollover gap:`
|
||||
dvakrát. 117 → 96 řádků.
|
||||
- **Nepřesnosti vůči skriptům** (všechny opraveny): `print_session()` má tři větve,
|
||||
dokumentovaná byla jedna (chybělo „window expired…" a samotné procento); Weekly řádek
|
||||
slíbený jako „resets in Y days", ale `fmt_delta_short()` pod 24 h tiskne hodiny;
|
||||
„report ends with `Session window:`" — ve skutečnosti končí `Last poll:`; zdroj klíče
|
||||
je env **pak** `workspace/.env`, ne jen `.env`.
|
||||
- **Tabulka `meta` — odpověď na otázku.** Zapisuje se do ní (`record_poll()` na všech
|
||||
třech cestách pollu: `ok`, `http_<kód>`, `unreachable`), ale je to **jeden řádek**
|
||||
(`id = 1`), ne log. Čte ji jen `print_poll_status()` → `Last poll: <ts> (status)`.
|
||||
Smysl: `samples` se plní jen při změně, takže bez `meta` nejde odlišit „nic se
|
||||
nezměnilo" od „poller je mrtvý / API vrací 401". Zůstává; zapisovat vzorek při každém
|
||||
pollu bylo dřív zamítnuto (knowledge „Zamítnuto: heartbeat v usage polleru"), `meta`
|
||||
je právě ta zmenšená náhrada. Opraven ale **popis** v SKILL.md i v docstringu polleru
|
||||
— tvrdil „records every poll, so a gap can be told apart from a failed poll", což
|
||||
naznačuje historii; přepsaný řádek rozsoudí jen liveness *teď*.
|
||||
- **Trigger fráze** ukotveny na „ollama" (`ollama usage/quota/credits/limits`, `how much
|
||||
of the ollama plan is left`, `ollama usage report`). `"usage history"` zrušeno a obecné
|
||||
`"quota"` zamítnuto uživatelem — netriggerovalo by, tahalo by skill na cizí dotazy.
|
||||
- **Verifikace na serveru:** `ollama_usage.py` → `Session: 2.8 %, resets 13:00 CEST
|
||||
(in 2 h 22 min)` / `Weekly: 16.2 %, resets in 5 days` + Models sekce — sedí se šablonou
|
||||
řádek po řádku. Report končí `Last poll: 2026-09-15T08:37:01+00:00 (ok)`, `meta` má
|
||||
jeden řádek s čerstvým timestampem. Verifikace odhalila i poslední nepřesnost: při
|
||||
jediném rolloveru v historii report žádné `Rollover gap:` řádky netiskne
|
||||
(`itertools.pairwise`) — formulace upravena na „from the second recorded rollover on".
|
||||
Nasazeno rsyncem, md5 lokál == server, vlastník `nanobot:nanobot`, bez restartu služby.
|
||||
|
||||
**Co zbývá:** Nic. Commit `a7a725e`.
|
||||
|
||||
## 2026-09-16 07:20 — Fix po přejmenování presetů `glm53`→`glm`, `kimi27`→`kimi`
|
||||
|
||||
**Cíl:** Uživatel přejmenoval aliasy modelů v serverovém `config.json`. Rozbily se dvě
|
||||
noční cron úlohy (`KeyError: "model_preset 'kimi27' not found"` z compact-memory,
|
||||
`'glm53'` z reflectu). Vrátit je do provozu a najít i místa, kde je rozbití tiché.
|
||||
|
||||
**Co jsem zkusil / co jsem našel:**
|
||||
|
||||
Dva paralelní skeny — lokální repo a server. Aktuální stav configu: 11 presetů, default
|
||||
`glm` (= `glm-5.3:cloud`), fallback `kimi` (= `kimi-k2.7-code:cloud`). Obě přejmenování
|
||||
jsou 1:1 na stejný model string, takže oprava je mechanická a joby poběží na tomtéž
|
||||
modelu jako dřív.
|
||||
|
||||
*Proč to nechytila validace:* `Config._validate_model_preset` (nanobot `config/schema.py`,
|
||||
model_validator mode="after") ověřuje při startu **jen** `agents.defaults.modelPreset` a
|
||||
`fallbackModels`. Obojí uživatel přejmenoval správně, takže gateway nastartovala bez
|
||||
chyby. Preset předaný ad-hoc do `Nanobot.from_config(model_preset=...)` — což dělají
|
||||
právě oba cron skripty — neprochází žádnou statickou kontrolou a spadne až za běhu
|
||||
v `Config.resolve_preset()`.
|
||||
|
||||
*Tichá část (to, co „není vidět"):* serverový `~/.nanobot/workspace/knowledge/models.md`
|
||||
je tabulka presetů, podle které si agent vybírá `/model`. Obsahovala neplatné aliasy —
|
||||
a navíc **neodpovídala configu ani před přejmenováním**: `glm` měl model id
|
||||
`glm-5.3-flash` (reálně `glm-5.3`, flash je samostatný preset `flash`), překlep `glmi51`
|
||||
místo `glm51`, chyběly presety `flash`, `sonnet`, `haiku`, `gemini-flash(-lite)`, a řádek
|
||||
„Kódování: `kimi27` (fallback: `kimi`)" byl po přejmenování sebereferenční nesmysl.
|
||||
Uživatel rozhodl srovnat celou tabulku s realitou a sjednotit daily driver i těžké úlohy
|
||||
na `glm` (glm-5.3); eskalační sekce „Kdy přepnout na glm53" tím ztratila smysl a je
|
||||
nahrazena „Kdy sáhnout po `flash`" (multimodální vstup — `glm` je text-only — a levné
|
||||
rutinní tahy).
|
||||
|
||||
*Co rozbité nebylo:* `skills/detach/` (resolvuje presety dynamicky přes
|
||||
`load_preset_names()`/`resolve_preset()`), `skills/usage/`, `cron/jobs.json`
|
||||
(dream/heartbeat/version-check preset nenastavují, `dream.modelOverride` je `null`),
|
||||
systemd unity, `workspace/USER.md` (`Preset: glm` je dál platné), fronty
|
||||
`tasks/{inbox,new,running}`. Komentáře v `reflect_auto.py:49` a `reflect_distill.py:55`
|
||||
zmiňují **model** `glm-5.3:cloud`, ne preset — zůstaly.
|
||||
|
||||
**Co fungovalo a proč:**
|
||||
|
||||
1. Nejdřív `rsync` serverového `knowledge/models.md` do repa vlastním commitem (`3d17bf8`) —
|
||||
lokální verze byla z 2026-06 a úplně jiný soubor, slepý přepis by serverovou verzi
|
||||
(přepsanou 2026-08-29) zahodil. Pravidlo „server je zdroj pravdy" se vyplatilo.
|
||||
2. `MODEL_PRESET` opraven na `"glm"` / `"kimi"`, `compact-memory/README.md` srovnán,
|
||||
`knowledge/models.md` přepsán proti reálnému `modelPresets` (commit `dcb66d5`).
|
||||
3. Nasazeno rsyncem celých adresářů skillů + `knowledge/models.md`, vlastník
|
||||
`nanobot:nanobot`, bez restartu služby.
|
||||
4. Verifikace: `Nanobot.from_config(model_preset=...)` pro `glm` i `kimi` proběhne a loguje
|
||||
přepnutí na `glm-5.3:cloud` / `kimi-k2.7-code:cloud` (přesně ta cesta, co padala);
|
||||
grep starých aliasů přes `skills/`, `knowledge/` a `config.json` je prázdný; reflect
|
||||
`--dry-run --max-batches 1` exit 0; lokálně 219 testů zelených.
|
||||
5. Grep zpočátku hlásil zbylý výskyt v `skills/reflect/scripts/__pycache__/*.pyc` —
|
||||
zastaralý bytecode ze 2. 9., smazán (Python si ho vygeneruje znovu).
|
||||
|
||||
**Gotcha pro příště:** `~/.local/bin/uv` plnou cestou — v neinteraktivním SSH není `uv`
|
||||
v `PATH` (`bash: uv: command not found`).
|
||||
|
||||
**Co zbývá:**
|
||||
|
||||
- E2E potvrzení nočního běhu (02:00 compact-memory, 03:30 reflect) — v logu nesmí být
|
||||
`KeyError` a má přijít Telegram report. `compact_memory_auto.py` nemá dry-run a mutuje
|
||||
`memory/MEMORY.md`, takže ruční spuštění jsem nedělal.
|
||||
- Mimo rozsah, hlášeno uživateli: `src/MiniLoop/config.json` je commitnutý v gitu včetně
|
||||
živého OpenRouter API klíče v plaintextu (řádek v `src/MiniLoop/.gitignore` je
|
||||
zakomentovaný). Vyžaduje rotaci klíče a odstranění z historie.
|
||||
- Automatický validátor presetů uživatel zamítl — Telegram alert z nočního běhu je jako
|
||||
detekce dostatečný. Postup ruční kontroly zapsán do `knowledge.md`.
|
||||
|
||||
@@ -260,12 +260,21 @@ Modely se přidávají jako položky do `model_presets` v `~/.nanobot/config.jso
|
||||
|
||||
4. **V chatu** (Telegram/WebUI) přepneš příkazem `/model <preset-name>`.
|
||||
|
||||
**Konvence pojmenování presetů:** krátký alias podle modelu — `kimi`, `kimi27`, `kimi3`, `glm`, `glm52`, `sonnet`, `haiku`, `gemini-flash`. (Dřív tu stálo `<model>-<provider>` jako `kimi-k2.6-openrouter`; reálný stav na serveru je od nějaké doby krátká forma, ověřeno 2026-07-27.)
|
||||
**Konvence pojmenování presetů:** krátký alias podle modelu. Aktuální sada (ověřeno 2026-09-16): `glm`, `flash`, `glm52`, `glm51`, `kimi`, `kimi26`, `kimi3`, `sonnet`, `haiku`, `gemini-flash`, `gemini-flash-lite`. **Nepojmenovaný alias = aktuální generace** (`glm` = glm-5.3, `kimi` = kimi-k2.7-code), číslovaný = zamrzlá starší verze. (Dřív tu stálo `<model>-<provider>` jako `kimi-k2.6-openrouter`, pak `glm53`/`kimi27`; 2026-09-16 přejmenováno na současnou formu.)
|
||||
|
||||
**Parametry presetu:** `maxTokens` 16384 a `temperature` 0.1 napříč všemi presety. `contextWindowTokens` se drží na **~97 % reálného okna modelu** (rezerva na výstup), reálné okno se čte z `curl http://nvidia.hell:11434/api/show -d '{"model":"<id>"}'` → `model_info["<family>.context_length"]`. `reasoningEffort: null` = zachovat default providera (`schema.py:141`), explicitní hodnota jen kde ji chceme vynutit (`glm52: high`).
|
||||
|
||||
**Ollama gotcha:** `providers.ollama.apiBase` musí končit `/v1` (`http://nvidia.hell:11434/v1`) — viz [[Ollama provider potřebuje `/v1` suffix v `apiBase`]].
|
||||
|
||||
**Gotcha — přejmenování presetu validace nechytí.** `Config._validate_model_preset` (nanobot `config/schema.py`, model_validator mode="after") ověřuje při startu **jen** `agents.defaults.modelPreset` a `fallbackModels`. Preset předaný ad-hoc do `Nanobot.from_config(model_preset=...)` neprojde žádnou statickou kontrolou a spadne až za běhu v `Config.resolve_preset()` — u nočních cron jobů tedy ve 2:00 do logu a Telegramu, ne při editaci configu. **Po každém přejmenování presetu projdi tahle místa:**
|
||||
|
||||
- `skills/reflect/scripts/reflect_auto.py` → `MODEL_PRESET`
|
||||
- `skills/compact-memory/scripts/compact_memory_auto.py` → `MODEL_PRESET`
|
||||
- serverový `~/.nanobot/workspace/knowledge/models.md` — tabulka presetů, podle které si agent vybírá `/model`; neplatný alias tam je tichá chyba (agent zkusí neexistující preset)
|
||||
- `skills/detach/` řešit nemusíš — preset resolvuje dynamicky přes `load_preset_names()`/`resolve_preset()`
|
||||
|
||||
Jednorázový check: `ssh nanobot@nanobot.hell 'grep -rn -e <starý-alias> ~/.nanobot/workspace/skills/ ~/.nanobot/workspace/knowledge/ ~/.nanobot/config.json'` (pozor na `__pycache__` — zastaralý `.pyc` matchne, ale nic neovlivní). Automatický validátor uživatel 2026-09-16 explicitně zamítl: Telegram alert z nočního běhu je jako detekce dostatečný. Plný záznam: history 2026-09-16.
|
||||
|
||||
## Ollama Cloud: některé modely jsou „extra usage only" (kimi-k3)
|
||||
|
||||
`kimi-k3:cloud` je v `api/tags` vidět a `/model kimi3` v nanobotu se přepne bez chyby, ale **každé volání skončí HTTP 402**:
|
||||
@@ -792,7 +801,7 @@ Nanobot má **hardcoded default `context_window_tokens = 65_536`** pro `ModelPre
|
||||
|
||||
Nastaveno 2026-06-02 per-preset na reálné limity modelů (kimi-k2.6 / qwen3.5 / nemotron-3-super 262144, minimax-m2.7 204800, glm-5.1 196608, deepseek-v4-flash 1048576) + `maxTokens` 16384. **Bez restartu** — `modelPresets` se hot-reloadují (viz sekce „Kdy je a není potřeba restart"). U `:cloud` modelů hostí kontext Ollama cloud, takže `contextWindowTokens` reálně rozšíří budget — není to lokální `num_ctx` žeroucí RAM. Plný záznam: history 2026-06-02.
|
||||
|
||||
**Gotcha — `agents.defaults.contextWindowTokens` je zavádějící číslo.** V serverovém `config.json` je `65536`, ale to platí jen když preset vlastní hodnotu nemá. Reálné okno aktuálního defaultu (**`glm53`: 976 000**, `glm52`/`glm-flash` 976 000, `kimi3` 1 020 000, `sonnet`/`gemini-flash` 256 000) je ~15× větší, než `defaults` napovídá. **Než z 65k něco odvodíš, přečti `model_presets`, ne `agents.defaults`** — na tomhle jsem 2026-09-02 postavil celý (zamítnutý) rozpočet velikosti pro `/project`. Limit, který v praxi kouše, je `maxToolResultChars: 16000`, ne okno.
|
||||
**Gotcha — `agents.defaults.contextWindowTokens` je zavádějící číslo.** V serverovém `config.json` je `65536`, ale to platí jen když preset vlastní hodnotu nemá. Reálné okno aktuálního defaultu (**`glm`: 976 000**, `flash`/`glm52` 976 000, `kimi3` 1 020 000, `sonnet`/`gemini-flash` 256 000) je ~15× větší, než `defaults` napovídá. **Než z 65k něco odvodíš, přečti `model_presets`, ne `agents.defaults`** — na tomhle jsem 2026-09-02 postavil celý (zamítnutý) rozpočet velikosti pro `/project`. Limit, který v praxi kouše, je `maxToolResultChars: 16000`, ne okno.
|
||||
|
||||
**Důsledky (trade-off, ne čistá výhra):**
|
||||
|
||||
@@ -1137,7 +1146,7 @@ Naměřeno na reálném destilátu (90 kB promptu → 75 135 tokenů podle tikto
|
||||
|
||||
## `contextWindowTokens` presetu přebíjí `agents.defaults`
|
||||
|
||||
`agents.defaults.contextWindowTokens` je 65536, ale preset `glm53` má 976000 a **vyhrává** — `agent/loop.py:476` (`context_window_tokens = extra.pop(...) or resolved.context_window_tokens`). Efektivní input budget = `contextWindowTokens - maxTokens - 1024` (`SNIP_SAFETY_BUFFER`, `agent/context_governance.py:105`), tedy ~958k tokenů pro glm53.
|
||||
`agents.defaults.contextWindowTokens` je 65536, ale preset `glm` má 976000 a **vyhrává** — `agent/loop.py:476` (`context_window_tokens = extra.pop(...) or resolved.context_window_tokens`). Efektivní input budget = `contextWindowTokens - maxTokens - 1024` (`SNIP_SAFETY_BUFFER`, `agent/context_governance.py:105`), tedy ~958k tokenů pro `glm`.
|
||||
|
||||
## Velká zpráva projde, `snip_history` krátí jen historii
|
||||
|
||||
@@ -1302,3 +1311,54 @@ per model. Ověřeno 2026-09-15.
|
||||
→ Atribuce spotřeby na session jde jen **časovou korelací** (timestampy v `sessions/*.jsonl`
|
||||
a `Processing message from …` v journalu) proti řadě vzorků z `db/ollama_usage.sqlite`.
|
||||
Ověřeno 2026-09-15, zdroj: [[plans/ollama-usage-poller.md]] sekce Revize.
|
||||
|
||||
---
|
||||
|
||||
## Ollama session okno: 5 h kotvených prvním requestem, ne pevná mřížka
|
||||
|
||||
Okno **nezačíná na pevné mřížce** — začíná prvním requestem po vypršení
|
||||
předchozího (jako 5h sessions v Claude Code). Naměřeno 2026-09-15: usage
|
||||
zůstala na `0.077`/21 req přes 05:00 UTC (pevná mřížka by ji tam vynulovala
|
||||
a poller by to zapsal) a resetovala se až s requestem v 06:00, po 93minutové
|
||||
pauze. Rekonstrukce aktivity z `sessions/*.jsonl` dává souvislý řetězec
|
||||
00:00–05:00, pak 06:00–11:00 UTC.
|
||||
|
||||
Délka 5 h je z primárního zdroje
|
||||
[transparent-pricing](https://ollama.com/blog/transparent-pricing) („no 5-hour
|
||||
or weekly limits" je to, co **nové** plány zrušily; tenhle klíč je pořád má).
|
||||
Že jde o okno a ne rolling counter je taky měřené — pokles `0.077`/21 → `0.0`/`{}`
|
||||
naráz, rolling by se rozpadal postupně.
|
||||
|
||||
**Praktický důsledek:** konec okna = poslední pozorovaný přechod v `samples`
|
||||
+ 5 h. Za konec se nesmí extrapolovat — další okno začne až dalším requestem,
|
||||
takže čas neexistuje, dokud request nepřijde. Detail: history 2026-09-15 10:15.
|
||||
|
||||
**Pozor na past:** crony nanobota běží na celé hodiny (Dream po 2 h atd.), takže
|
||||
první request po pauze často padne na celou hodinu. Hranice pak *vypadají* jako
|
||||
pevná mřížka, i když nejsou.
|
||||
|
||||
**Zamítnuto:** kalibrace fáze průnikem kandidátů modulo 300 min. Stála na
|
||||
předpokladu pevné mřížky a navíc potřebovala víc pozorování, než začala hlásit
|
||||
cokoli. Nahrazeno výše. Vedlejší poznatek, který z ní zbyl: **5 h nedělí 24 h**,
|
||||
takže „mřížka 00/05/10/15/20 UTC" nemůže být stálá tak jako tak.
|
||||
|
||||
## `limits.session.usage` == procento na dashboardu; countdown na webu ne
|
||||
|
||||
API `2.3 %` sedělo přesně s tím, co uživatel viděl na webu. **Countdown na webu
|
||||
je ale ke kalibraci nepoužitelný** — zaokrouhluje neznámo jak: v 06:58 UTC
|
||||
ukazoval „4 hodiny" a v 07:05 „3 hodiny". Proto skill tiskne i konkrétní čas,
|
||||
ne jen „resets in X".
|
||||
|
||||
## Zamítnuto: heartbeat v usage polleru
|
||||
|
||||
Nápad zapisovat vzorek i beze změny (aby mezera v `samples` znamenala výpadek)
|
||||
je **zbytečný** — interval `(poslední před, první po]` kolem resetu je pravdivý
|
||||
bez ohledu na to, jestli poller běžel; výpadek ho jen rozšíří. Uživatel ho
|
||||
zamítl a měl pravdu: DB zůstává čistě změnová.
|
||||
|
||||
## `uv` není v PATH při neinteraktivním SSH
|
||||
|
||||
`ssh nanobot@nanobot.hell 'uv run …'` selže na `uv: command not found` — je
|
||||
v `~/.local/bin`, který se nenačte. Crontab to řeší vlastním řádkem
|
||||
`PATH=/home/nanobot/.local/bin:/usr/bin:/bin`. Při ručním spuštění přes SSH
|
||||
předřadit `export PATH=/home/nanobot/.local/bin:$PATH`.
|
||||
|
||||
@@ -31,3 +31,60 @@ Po každém commitu, který mění `knowledge.md`, `history.md` nebo `memory.md`
|
||||
**Why:** Uživatel 2026-06-02 chtěl agentovi zpřístupnit develop kontext a zvolil průběžnou synchronizaci (ne jednorázovou kopii) — jinak agent časem uvidí zastaralý stav.
|
||||
|
||||
**How to apply:** `rsync -av <soubor> nanobot@nanobot.hell:/home/nanobot/.nanobot/workspace/develop/`. `README.md` v `develop/` je statický popis, ten se nesynchronizuje. Owner zůstává `nanobot:nanobot` (jdeme jako `nanobot`).
|
||||
|
||||
## feedback: než navrhneš řešení, nejdřív se podívej na reálný stav souborů na serveru
|
||||
|
||||
Když uživatel navrhuje změnu/přidání do serverové konfigurace (skill, `SOUL.md`/`AGENTS.md`/ostatní workspace soubory, config), **nejdřív si stáhni a přečti aktuální serverovou verzi** a ověř, zda navrhovaná věc už neexistuje nebo není vyřešená jinak — teprve pak navrhuj postup.
|
||||
|
||||
**Why:** Uživatel 2026-06-07 — na otázku „má smysl zapsat nanobotovi reasoning anglicky?" jsem rovnou navrhl formulaci a celý deployment, ale pravidlo už v `SOUL.md` dávno bylo (napsal si ho Dream procesor sám 2026-05-27). Celý návrh byl zbytečný. Uživatel to označil za podstatnější poznatek než samotnou odpověď. Server se navíc mění autonomně (Dream), takže předpoklady z paměti/repa můžou být zastaralé.
|
||||
|
||||
**How to apply:** U čehokoli, co se týká serverového stavu, je první krok `rsync`/`ssh cat` reálného souboru + kontrola, jestli problém už není vyřešený. Návrh řešení až po ověření. Platí i pro „malé" změny, které vypadají triviálně.
|
||||
|
||||
## feedback: u bugu nejdřív najdi a dolož PŘÍČINU, neiteruj workaroundy
|
||||
|
||||
Než navrhnu jakýkoli fix chování (zvlášť rendering/UI bug), musím **nejdřív najít a doložit kořenovou příčinu** — přečíst reálný **nasazený** kód, který chování produkuje (ne upstream/podobnou verzi), a získat **přímý důkaz** (např. session log s tím, co model skutečně vrátil). Teprve s prokázanou příčinou navrhovat řešení.
|
||||
|
||||
**Why:** 2026-06-14 (`/note` + URL ve WebUI, viz [[plans/note-wrong-urls.md]]) jsem několik kol nasazoval kosmetické obezličky (linkify → backtick → odrážky → tučné číslo), všechny selhaly, a teprve pak našel příčinu: custom fork WebUI má `li` handler, co přebalí každou položku seznamu s odkazem na kartu. Kdybych nejdřív přečetl nasazený renderer a session log (důkaz, že model echovuje verbatim → chyba je v rendereru, ne v modelu/formátu), ušetřil bych celá kola deploy-test a autorovo zklamání. Nabízená „řešení" pak byly workaroundy, ne systémová oprava — autor je všechny zamítl jako „nesystémové".
|
||||
|
||||
**How to apply:** U bug reportu: (1) lokalizuj a přečti reálný nasazený kód zodpovědný za chování; (2) seženi přímý důkaz, kde přesně se to láme (logy, session transcript, raw výstup) a vyluč nesprávné hypotézy (model vs renderer apod.); (3) až pak navrhuj fix — a měř ho proti příčině: pokud neopravuje příčinu, řekni to nahlas a označ za workaround. Doplňuje [[memory.md]] „než navrhneš řešení, nejdřív se podívej na reálný stav souborů na serveru".
|
||||
|
||||
## feedback: skill nemá opakovat ani vysvětlovat to, co už je v system promptu
|
||||
|
||||
Do `SKILL.md` (nanobotího skillu) nepatří konvence a fakta o prostředí, která už žijí
|
||||
v system promptu — `AGENTS.md` (nástroje, `uv`, temp soubory, exec guard, git) a `SOUL.md`
|
||||
(osobnost, styl výstupu, jazyk reasoningu). Skill popisuje **svůj vlastní postup**, ne to,
|
||||
jak se v tomhle prostředí obecně pracuje. Než něco takového do skillu napíšu, ověřit
|
||||
`grep` v `AGENTS.md`/`SOUL.md`, jestli to tam už není.
|
||||
|
||||
**Why:** 2026-09-02 jsem při zkracování skillu `reflect` do STOP gate 1 *přidal* půlvětu
|
||||
vysvětlující, proč se `uv` volá plnou cestou („`uv` není v `PATH` v neinteraktivním SSH;
|
||||
uvnitř tahu stačí `uv run`"). Uživatel se zeptal, proč to tam vůbec je — `AGENTS.md` má
|
||||
celou sekci `## python — use uv`. Byla to dvojí chyba: environmentální meta-znalost ve
|
||||
skillu, a druhá polovina věty navíc doslova opakovala, co `AGENTS.md` agentovi říká.
|
||||
Zvlášť trapné v commitu, jehož cílem bylo skill **zkrátit**. Viz `history.md` 2026-09-02
|
||||
14:15.
|
||||
|
||||
**How to apply:** Fakt o prostředí → `AGENTS.md`/`SOUL.md`. Vysvětlení „proč je ten příkaz
|
||||
takhle" pro člověka → `README.md` skillu (nenačítá se do kontextu, takže nestojí tokeny).
|
||||
Do `SKILL.md` jen to, co agent potřebuje k provedení **tohoto** postupu. A pozor na
|
||||
asymetrii: přidat do system promptu se vyplatí jen tehdy, když to agent reálně potřebuje —
|
||||
`PATH` gotcha se do `AGENTS.md` nakonec taky nepřidala, protože agentovi bare `uv run`
|
||||
funguje a týkala se jen člověka v SSH. Souvisí s [[memory.md]] „než navrhneš řešení,
|
||||
nejdřív se podívej na reálný stav souborů na serveru".
|
||||
|
||||
## feedback: YAML seznamy — víc položek nebo dlouhé stringy jdou do block stylu
|
||||
|
||||
Seznam s jedinou krátkou položkou zůstává flow (`include: ["*.md"]`, `paths: ["**"]`,
|
||||
`exclude: []`). Jakmile má **víc položek** nebo jsou položky **dlouhé stringy** (typicky
|
||||
cesty a globy), píše se **block stylem, každá položka na vlastním řádku**.
|
||||
|
||||
**Why:** 2026-09-09 jsem v plánu `final-wiki-hybrid-rag` napsal `exclude` jako flow seznam
|
||||
zalomený přes dva řádky (`["**/node_modules/**", "**/vendor/**",` / `"**/.venv/**", …]`).
|
||||
Uživatel to vrátil: zalomený flow seznam je nejhorší z obou světů — nevejde se na řádek,
|
||||
nejde u položky mít komentář a diff jedné změněné položky přepíše celý blok.
|
||||
|
||||
**How to apply:** Platí pro jakýkoli YAML, který píšu nebo který generuje kód (configy
|
||||
skillů, frontmatter, CI). Rozhoduj podle obsahu, ne podle délky výsledku: dva dlouhé globy
|
||||
jdou do block stylu, i kdyby se na jeden řádek vešly. Block styl navíc umožní komentář
|
||||
u konkrétní položky, což u whitelistů a excludů nese hodnotu. Souvisí s pravidlem
|
||||
v `CLAUDE.md`, že config je YAML právě kvůli komentářům.
|
||||
|
||||
Reference in New Issue
Block a user