---
slug: telegram-button-checkin
status: approved-for-execution
intent: clear
pending-action: execute .omo/plans/telegram-button-checkin.md
approach: Add a profile-gated Telegram callback/text workflow bridge, persistent profile-local wizard state, and a topic-bound scheduled launcher; retain the existing append-only event store as the sole final-record sink.
---

# Draft: telegram-button-checkin

## Components (topology ledger)
<!-- Lock the SHAPE before depth. One row per top-level component that can succeed or fail independently. -->
<!-- id | outcome (one line) | status: active|deferred | evidence path -->
| wizard-state | Restart-safe, owner/topic-bound check-in draft and finalization state | active | profile checkin_cli models/store |
| telegram-bridge | Profile-gated callback and text-reply dispatch through the existing Telegram gateway | active | gateway/platforms/telegram.py |
| scheduled-launch | Morning button card and daily idempotency without an additional Telegram poller | active | profile scripts + Hermes cron |
| training-flow | Separate post-workout record flow linked to the same owner/topic | active | profile event store |

## Open assumptions (announced defaults)
<!-- Record any default you adopt instead of asking, so the user can veto it at the gate. -->
<!-- assumption | adopted default | rationale | reversible? -->
| Morning required fields | weight, sleep duration/quality, condition, pain, calories, and today training; all other facts are optional | user requested a compact routine | yes |
| Workout detail | a separate user-triggered post-workout wizard records completion, short activity note, perceived quality, and pain | avoids making the morning flow burdensome | yes |
| Input modes | buttons for bounded choices and typed messages for numeric/free-text values | Telegram buttons cannot sensibly encode bodyweight/calories/training content | yes |
| Delivery boundary | only the configured owner + one configured group topic can start, click, resume, or finalize | profile is solo-use and topic-scoped | no |
| Data retention | draft answers live profile-local; only final confirmation writes a canonical check-in/correction event | preserves append-only history and prevents partial values affecting trends | no |

## Findings (cited - path:lines)

- `gateway/platforms/telegram.py:2117,3939-4278` already registers and routes inline callback queries, but has no generic profile workflow registry.
- `gateway/platforms/telegram.py:3393-3473,4144-4247` proves topic-aware numbered inline buttons, authorization checks, callback acknowledgement, and stale-click handling patterns.
- `cron/scheduler.py:116-122` prevents scheduled agents from using the interactive clarify path; the existing no-agent morning job must therefore launch a button card directly rather than rely on LLM clarification.
- `workspace/checkin_cli/checkin_cli/store.py` is the established append-only final event boundary; partial wizard data must not be written as live trend data.
- Metis review: `cron/scheduler.py:1402-1429` delivers script stdout as plain text, so scheduled keyboard cards require a narrow scheduler-to-Telegram-card capability.
- Metis review: `cron/scheduler.py:991-1012` resolves relative scripts beneath `HERMES_HOME/scripts`; active profile jobs currently contain an extra `scripts/` path component and must be migrated safely.
- Metis review: legacy events cannot represent sleep quality, condition, pain, or workout-only finalization; a versioned wizard event contract is required.

## Decisions (with rationale)

- Use a compact callback payload carrying only protocol/version, opaque session ID, expected step, and action; never encode answers in Telegram callback data.
- Add a thin generic gateway dispatch seam that is dormant unless the isolated physique profile enables it; do not create a second getUpdates poller or alter default Hermes behavior.
- Bind every start/click/text answer to exact owner, chat, and forum thread values from the isolated profile configuration.
- Use at-most-once scheduled-card semantics: persist a KST launch claim before sending. A post-claim failure recovers through manual Start/Resume rather than duplicate automatic sends.
- Urgent draft input gives immediate safety guidance without creating a trend-eligible record; an explicit acknowledgement may append a non-eligible safety audit.

## Scope IN

- Morning six-step button wizard, optional note, final summary/save/modify/resume behavior.
- Separate on-demand post-workout wizard.
- Durable profile-local state, idempotency, correction linkage, safety branch, and active scheduled button launcher.
- Profile-gated generic Telegram callback/text dispatch addition and regression tests.

## Scope OUT (Must NOT have)

- No second Telegram bot, poller, webhook, or token reuse.
- No changes to default/trading profile prompts, data, routing, or cron jobs.
- No raw check-in values in callback payloads, logs, or source-update queue.
- No drug/hormone/medical/extreme-cut advice.

## Open questions

- None. User approved the compact six-step morning flow and separate post-workout flow.

## Approval gate
status: approved by user message “ㅇㅋ 진행해봐” after the compact-flow review.
<!-- When exploration is exhausted and unknowns are answered, set status: awaiting-approval. -->
<!-- That durable record is the loop guard: on a later turn read it and resume at the gate instead of re-running exploration. -->
