Files
nanobot-runtime/results/remind-skill-audit-2026-06-02.md
2026-06-10 06:39:52 +02:00

7.3 KiB

Deep Research Audit: /remind Skill

Datum: 2026-06-02
Model: GLM-5.1:cloud
Scope: SKILL.md, remind_edit.py, remind_send.py, random_times.py, testy, reminder.yaml, .reminder_state.json, log, crontab


🔴 Kritické problémy

1. Žádný update příkaz

remind_edit.py má jen list, add, remove. Když chceš změnit čas existujícího reminderu, musíš ho smazat a vytvořit znovu. To je nebezpečné — remove matchuje substring, takže při recreate můžeš trefit špatný záznam nebo vytvořit duplikát.

Návrh: Přidat update --keyword "..." --cron/--at/--random-* příkaz, který najde reminder a upraví jen zadaná pole.

2. at remindery se nikdy nesmažou (no garbage collection)

Jednorázové at remindery zůstávají v reminder.yaml navždy. Po odeslání se jen přestanou spouštět, ale leží v YAML a loadují se každý minutovým cronem. Po čase tam bude stovky mrtvých záznamů.

Návrh: remind_send.py by měl po úspěšném doručení at reminderu zapsat flag nebo rovnou zavolat remind_edit.py remove. Nebo lépe — přidat purge subcommand, který smaže všechny at remindery s at_time < now.

3. Žádné stabilní ID — remove matchuje substring

remove --keyword "boty" by smazal "objednat boty xshoes", ale taky "koupit boty pro dědu". Substring match na textu je křehký.

Návrh: Přidat id pole (hash nebo UUID) přiřazené při add. remove i update by primárně pracovaly s --id. --keyword by zůstal jako fallback.

4. Deduplikace přes SHA1(text)[:8] — kolize a křehkost

.reminder_state.json klíč je hashlib.sha1(text.encode())[:8] — 4 bajty hex. Při ~65k reminderů je kolize pravděpodobná. Horší: když se text změní (i jen překlep), dedup key se změní a reminder se odešle znovu.

Návrh: Použít stabilní id z bodu 3 jako klíč do state. SHA1[:8] zahodit.


🟡 Střední problémy

5. Hardcoded CHAT_ID v remind_send.py

CHAT_ID = "8826147089" je natvrdo v kódu. Když se změní uživatel nebo přidá druhý, musí se upravovat zdroják.

Návrh: Číst chat_id z config.json (tam už je token), nebo z reminder.yaml jako globální default_chat_id.

6. Žádná validace at časů v budoucnosti

remind_edit.py přijme --at "2020-01-01T00:00:00" bez chyby. Zápis v minulosti nedává smysl a nikdy se nespustí.

Návrh: Validovat at > now() v cmd_add. Případně alespoň varování na stderr.

7. Žádný max-retry / TTL pro neodeslané remindery

Když Telegram API vrátí chybu, remind_send.py zkusí znovu příští minutu — ale jen pokud last state nebyl nastaven. Když selže 100x po sobě, zkusí to 100x. Žádný TTL ani exponential backoff.

Návrh: Přidat retry count do state. Po 3 selháních označit jako failed a přestat zkoušet. Nebo jednoduše: po 5 minutách od first fire time přestat retryovat.

8. Identity check bug v cmd_remove

data["reminders"] = [r for r in data["reminders"] if r is not removed]

is not je identity check. Funguje, protože matches[0] je reference na stejný dict v seznamu, ale je to křehké — jakýkoliv refaktoring (deep copy, reload) to rozbije.

Návrh: Použít index nebo id-based filter.

9. Chybí dokumentace k at_times (multi-at)

SKILL.md dokumentuje --at jako "repeatable", ale remind_send.py zpracovává at_times pole, zatímco SKILL.md ho nezmíní jako samostatný koncept. Uživatel (nebo LLM) může být zmatený.

Návrh: Doplnit SKILL.md o příklad multi-at.


🔵 Zlepšení kódu

10. Přechod z YAML na SQLite

YAML je lidsky čitelný, ale:

  • Atomic write přes .tmp + os.replace je správný, ale zbytečně složitý
  • YAML nemá schema, snadno se rozbije ruční editací
  • Dotazy (list, search) vyžadují full load

Návrh: Přesunout data do db/reminders.sqlite (konvence db/*.sqlite). YAML nechat jako read-only export nebo zahodit. remind_edit.py by pracoval s SQLite, remind_send.py taky. Výhody: ID autoincrement, atomicity zdarma, snadný search, žádný parse overhead.

11. Cachování Telegram tokenu

_telegram_token() čte a parsuje config.json každou minutu. Soubor se nemění.

Návrh: Načíst jednou při startu, cachovat v modulu. Nebo ještě lépe — environment variable TELEGRAM_BOT_TOKEN.

12. Log enrichment

reminder.log má jen timestamp text. Chybí: delivery status, fire time vs actual send time, reminder ID.

Návrh: Formát: {ts} {id} {fire_time} {status} {text}

13. --dry-run flag pro add

Užitečné pro LLM skill workflow — ukáže, co by se přidalo, bez zápisu.

14. Test coverage — chybí testy pro remind_edit.py a remind_send.py

Testy pokrývají jen random_times.py. remind_edit.py (CRUD) a remind_send.py (dedup, fire detection) nemají žádné testy.

Návrh: Přidat unit testy pro:

  • cmd_add s různými kombinacemi flagů
  • cmd_remove s 0/1/N matches
  • _due_fire s různými typy reminderů
  • Dedup state management

🟢 Chybějící funkce

15. Pause / disable reminder

Nemáš způsob jak reminder dočasně vypnout bez smazání. Běžný use case: "nech mě týden na pokoji".

Návrh: Přidat enabled: true/false pole. remind_send.py by skipoval enabled: false. Příkaz remind_edit.py pause --id X / resume --id X.

16. Cron s end date

Cron remindery běží navždy. Chybí until datum pro cron (podobně jako randomfrom/until).

Návrh: Přidat until pole na úroveň reminderu. remind_send.py by po until datumu reminder přeskočil.

17. Snooze

Když reminder přijde a uživatel není připraven, nemá jak ho odložit. To by vyžadovalo interakci s Telegram botem (callback button), což je mimo současný scope, ale je to přirozené rozšíření.

18. list --due nebo list --next

Užitečné zobrazit jen remindery, které se spustí v následujících N hodin. SKILL.md to neumožňuje.

Návrh: Přidat list --due-within 2h nebo list --next 5.

19. Per-reminder timezone

SKILL.md říká "Timezone is always Europe/Prague". To je OK pro jednoho uživatele, ale kód je tight-coupled — TZ je konstanta v remind_send.py. Pro multi-user by to muselo být konfigurovatelné.


📋 Prioritizovaný implementační plán

Priorita Co Proč
P0 Stabilní ID + dedup fix (body 3, 4) Bez toho hrozí kolize a duplikátní doručení
P0 Garbage collection at reminderů (bod 2) YAML poroste donekonečna
P0 Identity check fix v remove (bod 8) Tichý bug, dnes funguje náhodou
P1 update příkaz (bod 1) Zásadní UX zlepšení, snižuje riziko chyb
P1 Validace at v budoucnosti (bod 6) Prevence nesmyslných vstupů
P1 Retry TTL (bod 7) Prevence nekonečných retry
P2 SQLite backend (bod 10) Architektonické zlepšení, ale není urgentní
P2 Testy pro edit/send (bod 14) Spolehlivost
P2 pause/resume (bod 15) Užitečná funkce
P2 until pro cron (bod 16) Užitečná funkce
P3 Chat ID z configu (bod 5) Multi-user příprava
P3 Log enrichment (bod 12) Debugovatelnost
P3 --dry-run (bod 13) Vývojářská ergonomie
P3 list --due (bod 18) Nice-to-have