4.8 KiB
/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.jsonpouží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í.removematchuje substring —remove --keyword "boty"smaže i "koupit boty pro dědu".cmd_removepoužíváis notidentity check — funguje jen díky referenční shodě, po refaktoringu (deep copy, reload) se rozbije.
Řešení:
- Přidat
idpole (UUID nebo short hash z text+timestamp) přiřazené přiadd. removeiupdateprimárně přes--id,--keywordjako fallback.- State file klíč → stabilní
idmísto SHA1[:8]. cmd_removefiltrovat přesidnebo index, ne přesis 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é
atremindery zůstávají vreminder.yamlnavž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ůžeatreminder 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.pypo úspěšném doručeníatreminderu: buď ho smazat z YAML, nebo přidatpurgesubcommand pro ruční cleanup.- Zužit existující state file pro dedup: zúžit okno na
0 <= delta < 30a 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)sPRIMARY 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.yamlje 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 ayaml.dump()celého dokumentu. Použítruamel.yaml.scalarstring.LiteralScalarStringpří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.pymá jenaddaremove. Změna času = smazat a vytvořit znovu — rizikové (viz bod 1, substring match).listje 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-runkaddaupdatepro 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_adds různými kombinacemi flagů (cron, at, random)cmd_removes 0/1/N matches, substring kolizeshould_fires 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
atv 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.