7.9 KiB
reflect — jak to funguje
Skill hledá v mých vlastních session logách opakující se chyby, pojmenuje je a navrhne opravu. Nálezy pak procházíš ty, jeden po druhém, a rozhoduješ, co se použije. Sám od sebe nikdy nic nezmění.
Dva režimy
ANALÝZA — denně 03:30, bez tebe REVIEW — jen když napíšeš /reflect
cron → reflect_auto.py ├─ vezme 1 otevřený nález
├─ destiluje session z okna ├─ ukáže diagnózu, důkazy, návrh, diff
├─ LLM tah → nálezy ├─ čeká na tvoje rozhodnutí
├─ zapíše do findings.jsonl ├─ aplikuje → git commit → audit
└─ Telegram (jen když je co) └─ další nález
NEEDITUJE NIC edituje jen to, co schválíš
Nálezy jsou o nedávném chování, ne o celé historii
Běh se dívá jen na posledních 21 dní (--window-days). Není to úspora — je to
podmínka, aby nález něco znamenal: korpus má 3,5 měsíce a jedna dávka 200 kB, takže
dohánění celé historie po jedné dávce za noc znamenalo, že cursor tři noci stál na
konci května a nálezy z něj se předkládaly jako aktuální.
Dvě hranice, každá jinak:
| Co dělá | |
|---|---|
| cursor | podlaha — co se jednou analyzovalo, se neanalyzuje znovu (jinak by se zdvojily počty) |
| okno | strop — co je starší než 21 dní, se přeskočí a cursor to mine natrvalo |
Kdybys někdy potřeboval archeologii, vrať cursor ve state.json a spusť
--window-days 0.
Proč tě to neotravuje každý den
Denně přibude jen pár session — v nich se vzor nepozná, jedna chyba je náhoda. Proto:
| Kdy | Status | Telegram |
|---|---|---|
| vzor poprvé, 1 výskyt | watch |
ne, jen se počítá |
| vzor podruhé (≥2× a ve ≥2 session) | open |
ano |
| vzor už jednou opravený se vrátí po opravě | open + regrese |
ano |
| vzor už jednou opravený, ale důkazy jsou starší než oprava | watch |
ne |
| vzor jsi zamítl | watch napořád |
ne, už nikdy |
Zamítnutí je rozhodnutí, ne odklad — zamítnutý vzor se znovu neotevře.
Co ti /reflect nabídne
| Napíšeš | Stane se |
|---|---|
ok / aplikuj |
patch se použije a commitne |
uprav: <text> |
přepíšeš návrh vlastními slovy, ukáže se nový diff |
přeskoč |
nález zůstane otevřený na příště |
zamítni |
nález se zavře natrvalo |
konec |
konec review |
Nález, který od analýzy patch nedostal (většina), si ho složí až při review: agent napíše
návrh do JSON a nechá ho ověřit (--set-patch). Skript ho nejdřív ověří proti souboru
a teprve pak uloží, takže nepoužitelný pokus ve findings.jsonl nezůstane — a agent do
store nesahá vůbec. Pak ti ukáže diff a čeká na ok jako u každého jiného patche.
Vždy jen jeden nález najednou. Nálezy se číslují 1..N podle pořadí, ne podle
interního id.
Kde co leží
| Cesta | Co |
|---|---|
reflect/findings.jsonl |
všechny nálezy, jeden JSON na řádek |
reflect/state.json |
kam se došlo (cursor), použité okno + statistiky běhů |
results/<datum>_reflect.md |
plný report jednoho běhu + okno, které pokryl, a kolik výskytů na 100 session mají už známé vzory |
log/reflect.log |
audit — každé rozhodnutí, i zamítnutí a přeskočení |
„Naposledy" u nálezu je nejnovější datum v důkazech, ne datum, kdy se záznam
naposledy přepsal. Když je starší než okno posledního běhu, je nález zastaralý:
analýza tam už nedohlédne, takže se sám nikdy neobnoví — spíš než patch si zaslouží
zamítnutí. /reflect ti to u něj řekne.
Počty v nálezu (7× ve 4 session) jsou kumulativní součet napříč běhy — sečtené
z toho, co model napočítal v jednotlivých oknech, ne měření nad celým korpusem. Skript
u nich hlídá jen to, co hlídat umí: vzor nemůže zasáhnout víc session, než kolikrát
nastal, ani víc, než kolik jich v dávce vůbec bylo.
Co se o tvém rozhodnutí zapíše
| Rozhodnutí | Do findings.jsonl |
Do log/reflect.log |
|---|---|---|
| složení patche | patch + patch_drafted_at (= složeno při review, ne modelem) |
DRAFTED <id> [vzor] <soubor> |
ok |
applied: {at, sha, file}, patch = návrh modelu |
APPLIED <id> [vzor] <soubor> — <sha> |
uprav: |
navíc applied.new_text = tvoje verze (návrh modelu zůstává v patch) |
APPLIED-EDITED … |
zamítni |
rejected: {at, reason} — důvod je povinný |
REJECTED <id> [vzor] — <důvod> |
přeskoč |
skipped: {count, last}, status zůstává open |
SKIPPED <id> [vzor] ×N |
Povinný důvod u zamítnutí není otravování: je to jediná zpětná vazba na kvalitu analýzy. Z prvních osmi nálezů jsi čtyři zamítl — bez důvodů se z toho čísla nedá poznat, co v analytickém promptu změnit.
Na dotaz „co jsem už rozhodl" ti to /reflect vypíše z logu včetně revert příkazu.
Jak vrátit změnu zpět
Workspace je git, takže každá aplikovaná oprava je samostatný commit:
git revert <sha>
SHA najdeš v log/reflect.log nebo u nálezu ve findings.jsonl (applied.sha).
Změna žije jen na serveru — do trackovacího repa (src/nanobot) se musí dotáhnout
zvlášť, jinak ji příští rsync skillu přepíše zpátky.
Ruční spuštění
cd ~/.nanobot/workspace
# normální běh (to dělá cron)
~/.local/bin/uv run --script skills/reflect/scripts/reflect_auto.py
# jiné okno než výchozích 21 dní
~/.local/bin/uv run --script skills/reflect/scripts/reflect_auto.py --window-days 7
# jen prompty do tmp/, nevolat model; s --all přes celý korpus
~/.local/bin/uv run --script skills/reflect/scripts/reflect_auto.py --dry-run --all
# jak velký je korpus a na kolik dávek vyjde (nevolá model)
~/.local/bin/uv run --script skills/reflect/scripts/reflect_distill.py --stats > /dev/null
--all ignoruje cursor, takže by znovu přečetl už spočítané session a nafoukl jim
počty. V ostrém běhu ho skript odmítne — je jen na --dry-run.
Přerušený běh o hotovou práci nepřijde. Nálezy i cursor se zapisují po každé dávce
a nová dávka nezačne po 20 minutách (--deadline-minutes). S 21denním oknem to vyjde
zpravidla na jednu dávku, takže se tenhle strop ani neuplatní; kdyby zbyla nezpracovaná
dávka, Telegram to řekne i když nálezy nejsou žádné.
Plná cesta k uv je tu proto, že v neinteraktivním SSH není v PATH. Crontab si
PATH nastavuje sám, takže tam stačí uv run ….
Záruky
Nejsou to sliby v promptu, ale kód:
- Analýza (
reflect_auto.py) nemá v sobě žádnou cestu k zápisu do cizího souboru. Navíc se před a po tahu porovnágit statuscelého workspace — kdyby agent přesto něco zapsal, nálezy se zahodí. - Editaci dělá výhradně
reflect_apply.py, vždy jeden nález. Agent soubor needituje sám. Skript odmítne patch, jehož původní text v souboru není nebo je tam vícekrát — nehádá, kam patřil. Odmítne i nález, který neníopen(tj. nebyl ti předložen). - Do
findings.jsonlpíše taky jenreflect_apply.py. I patch složený při review jde přes něj (--set-patch) a projde stejnou kontrolou; agent auditní stopu needituje. - Commituje se jen ten jeden dotčený soubor (
git add -- <file>), nikdygit add -A. Rozdělaná práce Dreamu a jiných skillů se do commitu nedostane; když je rozdělaný přímo ten soubor, udělá se nejdřív checkpoint commit, aby byl revert přesný. - Skill vynechává vlastní session, takže neanalyzuje sám sebe.
- Každé rozhodnutí nechá záznam, i to, které nic nezmění: zamítnutí s důvodem,
přeskočení s počítadlem,
uprav:odděleně od původního návrhu modelu.
Ověřeno testy v tests/ — včetně toho, že git revert vrátí soubor do původního stavu.