Zalohovani vsech podstatnych souboru
This commit is contained in:
87
results/remind-skill-top5-priorities-2026-06-02.md
Normal file
87
results/remind-skill-top5-priorities-2026-06-02.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# /remind Skill — Top 5 Priorities (Merged from Two Audits)
|
||||
|
||||
**Datum:** 2026-06-02
|
||||
**Model:** GLM-5.1:cloud
|
||||
**Zdroje:** `results/2026-06-02_remind-skill-analysis-and-improvements.md` + `results/remind-skill-audit-2026-06-02.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. Stabilní ID + dedup fix (nahradit SHA1[:8] + substring match)
|
||||
|
||||
**Problém:** Dva propojené bugy:
|
||||
- `.reminder_state.json` používá `SHA1(text)[:8]` jako dedup klíč — 4 bajty hex, kolize při ~65k reminderů. Změna textu (i překlep) vytvoří nový klíč → duplikátní doručení.
|
||||
- `remove` matchuje substring — `remove --keyword "boty"` smaže i "koupit boty pro dědu".
|
||||
- `cmd_remove` používá `is not` identity check — funguje jen díky referenční shodě, po refaktoringu (deep copy, reload) se rozbije.
|
||||
|
||||
**Řešení:**
|
||||
- Přidat `id` pole (UUID nebo short hash z text+timestamp) přiřazené při `add`.
|
||||
- `remove` i `update` primárně přes `--id`, `--keyword` jako fallback.
|
||||
- State file klíč → stabilní `id` místo SHA1[:8].
|
||||
- `cmd_remove` filtrovat přes `id` nebo index, ne přes `is not`.
|
||||
|
||||
**Dopad:** Zabrání tichým datovým ztrátám a duplikátům. Bez toho je celý skill nespolehlivý.
|
||||
|
||||
---
|
||||
|
||||
## 2. Garbage collection `at` reminderů + deduplikace při doručení
|
||||
|
||||
**Problém:** Dva propojené bugy:
|
||||
- Jednorázové `at` remindery zůstávají v `reminder.yaml` navždy. Po odeslání se jen přestanou spouštět, ale loadují se každý minutovým cronem. Po měsících tam budou stovky mrtvých záznamů.
|
||||
- `should_fire()` má 60s okno — s minutovým cronem může `at` reminder doručit dvakrát (např. při dvojím spuštění cronu nebo časovém posunu). Log ukazuje, že to zatím proběhlo OK, ale není to garantováno.
|
||||
|
||||
**Řešení:**
|
||||
- `remind_send.py` po úspěšném doručení `at` reminderu: buď ho smazat z YAML, nebo přidat `purge` subcommand pro ruční cleanup.
|
||||
- Zužit existující state file pro dedup: zúžit okno na `0 <= delta < 30` a kontrolovat, zda už byl ve stejném minutovém okně doručen (state file už existuje, jen má špatný klíč — viz bod 1).
|
||||
- Alternativně: SQLite state tabulka `fired(text, scheduled_at, fired_at)` s `PRIMARY KEY(text, scheduled_at)`.
|
||||
|
||||
**Dopad:** Zabrání spamu a nekonečnému růstu YAML souboru.
|
||||
|
||||
---
|
||||
|
||||
## 3. Atomic writes + odstranění ruční YAML konstrukce
|
||||
|
||||
**Problém:** Dva propojené problémy v `remind_edit.py`:
|
||||
- Zápis do `reminder.yaml` je neatomický — `with open(REMINDER_FILE, "w")` může při crashu zanechat prázdný/s poškozený soubor = ztráta všech reminderů.
|
||||
- `format_reminder()` ručně skládá YAML stringy (`f"- text: {text}"`) — neescapuje speciální znaky (uvozovky, dvojtečky, newlines), nedrží konzistentní odsazení, duplikuje logiku ruamel.yaml.
|
||||
|
||||
**Řešení:**
|
||||
- Atomic write: `tmp = path.with_suffix(".tmp")` → `yaml.dump(data, f)` → `os.replace(tmp, path)`.
|
||||
- Nahradit `format_reminder()` builděním dictu a `yaml.dump()` celého dokumentu. Použít `ruamel.yaml.scalarstring.LiteralScalarString` přímo z knihovny (smazat vlastní třídu).
|
||||
- Přidat validaci před zápisem (schema check).
|
||||
|
||||
**Dopad:** Zabrání ztrátě dat a tichým YAML parse chybám. Největší robustness win s minimálním úsilím.
|
||||
|
||||
---
|
||||
|
||||
## 4. `update` příkaz + `list` příkaz
|
||||
|
||||
**Problém:**
|
||||
- `remind_edit.py` má jen `add` a `remove`. Změna času = smazat a vytvořit znovu — rizikové (viz bod 1, substring match).
|
||||
- `list` je dokumentovaný v SKILL.md, ale v kódu neexistuje. Uživatel (nebo LLM) nemá jak zkontrolovat aktuální stav.
|
||||
|
||||
**Řešení:**
|
||||
- Přidat `update --id X [--cron ...] [--at ...] [--random-* ...]` — najde reminder a upraví jen zadaná pole.
|
||||
- Přidat `list` — vypíše všechny remindery s ID, textem a typem schedule.
|
||||
- Přidat `--dry-run` k `add` a `update` pro bezpečné testování.
|
||||
|
||||
**Dopad:** Zásadní UX zlepšení, snižuje riziko chyb při úpravách, doplňuje chybějící dokumentovanou funkci.
|
||||
|
||||
---
|
||||
|
||||
## 5. Testy pro `remind_edit.py` a `remind_send.py`
|
||||
|
||||
**Problém:** Testy pokrývají jen `random_times.py`. Dva hlavní skripty (CRUD operace, dedup, fire detection, YAML I/O) nemají žádné testy. Jakákoliv změna v bodech 1–4 bez testů = riziko regresí.
|
||||
|
||||
**Řešení:** Přidat unit testy pro:
|
||||
- `cmd_add` s různými kombinacemi flagů (cron, at, random)
|
||||
- `cmd_remove` s 0/1/N matches, substring kolize
|
||||
- `should_fire` s různými typy reminderů a okraji časových oken
|
||||
- Dedup state management (nový i starý formát)
|
||||
- Atomic write (crash uprostřed zápisu)
|
||||
- Validace `at` v budoucnosti
|
||||
|
||||
**Dopad:** Bez testů je jakýkoliv refaktoring hazard. S testy se body 1–4 dají implementovat bez strachu z regresí.
|
||||
|
||||
---
|
||||
|
||||
*Zbylé návrhy (SQLite backend, pause/resume, cron until, retry TTL, log enrichment, chat_id z configu, per-reminder TZ) jsou P2–P3 — užitečné, ale nejsou blokátory.*
|
||||
Reference in New Issue
Block a user