165 lines
7.9 KiB
Markdown
165 lines
7.9 KiB
Markdown
# 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
|
||
|
||
```text
|
||
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:
|
||
|
||
```bash
|
||
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í
|
||
|
||
```bash
|
||
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 status` celé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.jsonl` píše taky jen `reflect_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>`), nikdy `git 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.
|