# telegram-button-checkin - Work Plan

## TL;DR (For humans)
<!-- Fill this LAST, after the detailed plan below is written, so it summarizes the REAL plan. -->
<!-- Plain English for a non-engineer: NO file paths, NO todo numbers, NO wave/agent/tool names. -->

**What you'll get:** Every morning, the physique topic will receive a Start/Resume button. One tap leads through a short six-part check-in; training is recorded separately after a workout. The bot saves only the final confirmed record and can safely resume if interrupted.

**Why this approach:** It keeps the daily routine fast while preserving trustworthy history. The existing Telegram gateway remains the only update receiver, and all button actions are restricted to the dedicated owner and topic.

**What it will NOT do:** It will not touch the existing trading Hermes, create a second bot poller, or place health/training values inside button payloads.

**Effort:** Medium
**Risk:** Medium - the feature extends the shared Telegram callback router, but it is inert outside the dedicated profile.
**Decisions to sanity-check:** Compact six-step morning check-in; a separate workout record; final-save-only event creation.

Your next move: Implementation starts now, as you requested. Full execution detail follows below.

---

> TL;DR (machine): Medium-risk, profile-gated Telegram wizard with durable final-save-only check-ins and topic-bound scheduled launch.

## Scope
### Must have

- A morning, topic-scoped inline-button check-in wizard with the approved compact fields: bodyweight; sleep duration and quality; condition; pain/urgent signals; prior-day calories; and today’s training status/body part. It must end with optional note, summary, save, edit, and resume.
- A separate, owner-triggered post-workout wizard: planned completion state, concise performed-training note, perceived quality, and pain.
- State survives process restart and duplicate/stale callbacks, with no partial draft affecting trend views. Final save appends through the existing profile event store; corrections preserve `supersedes` history.
- The isolated profile’s existing active 08:11 KST job sends the actual button launcher to its configured Telegram forum topic. No second Telegram poller is introduced.
- Core Hermes receives only a profile-gated generic dispatch seam plus a narrow scheduler-to-Telegram-card sender; default/trading behavior remains unchanged when the wizard is disabled or absent.
### Must NOT have (guardrails, anti-slop, scope boundaries)

- No raw body/health/training data in callback payloads, logs, or public queue files.
- No outbound delivery to any user/chat/thread outside the profile owner and configured topic.
- No token, default Hermes configuration, existing Telegram bridge, or trading state edits.
- No automatic health, medication, hormone, water/sodium, or extreme-cut prescription.

## Verification strategy
> Zero human intervention - all verification is agent-executed.
- Test decision: TDD + pytest plus hermetic Telegram adapter fakes; no live Telegram API test until the final user-owned acceptance message.
- Evidence: `.omo/evidence/telegram-button-checkin/task-<N>.md`.

## Execution strategy
### Parallel execution waves
> Target 5-8 todos per wave. Fewer than 3 (except the final) means you under-split.

Wave 1: 1. Wave 2: 2. Wave 3: 3. Wave 4: 4. Each wave is serialized because the callback bridge consumes the wizard’s typed domain contract and the active cron launcher consumes both.

### Dependency matrix
| Todo | Depends on | Blocks | Can parallelize with |
| --- | --- | --- | --- |
| 1 | none | 2, 3 | none |
| 2 | 1 | 3 | none |
| 3 | 1, 2 | final verification | none |
| 4 | 1, 2, 3 | final verification | none |

## Todos
> Implementation + Test = ONE todo. Never separate.
<!-- APPEND TASK BATCHES BELOW THIS LINE WITH edit/apply_patch - never rewrite the headers above. -->
- [x] 1. Build the profile-local persistent compact check-in and post-workout wizard domain
  What to do / Must NOT do: Version the profile event contract so it can represent a final `morning_checkin`, a non-trend-eligible `safety_audit`, and a final `workout_record` without requiring bodyweight/calories for a workout. Implement typed, owner/topic-bound wizard sessions and atomic owner-only persistence under `/home/cube/.hermes/profiles/physique-coach/workspace/checkin_cli`. Support the six approved morning fields, optional note, summary/save/edit/resume, separate post-workout flow, stale/duplicate action rejection, safety stop, and correction handoff. Do not write partial drafts into current/weekly trends, include personal values in callback payloads, or modify default Hermes.
  Parallelization: Wave 1 | Blocked by: none | Blocks: 2, 3
  References (executor has NO interview context - be exhaustive): `workspace/checkin_cli/checkin_cli/models.py`, `store.py`, `cli.py`; profile `contracts/checkin-event.schema.json`; existing tests under `workspace/checkin_cli/tests`; `gateway/platforms/telegram.py:3393-3473,4144-4247` for state/idempotency semantics.
  Acceptance criteria (agent-executable): red-first tests prove one canonical final event only after save; a morning event includes bodyweight/sleep duration+quality/condition/pain/calories/training plan while a workout event is valid without weight/calories and cannot pollute weight/calorie trends; same KST day resumes the same draft; wrong owner/topic, stale step, duplicate callback, invalid weight/calories, and urgent symptom cannot advance/duplicate a record; restart reloads the exact next step; correction links to the prior event. Urgent drafts give an immediate response and only explicit acknowledgement may append a non-eligible safety audit. Every new Python file is <=250 LOC and typed.
  QA scenarios (name the exact tool + invocation): happy: invoke the profile-local wizard CLI/state seam against a temporary home through start→answers→summary→save and inspect one final schema-valid event; failure: invoke stale/foreign/duplicate/urgent inputs and assert no eligible trend mutation. Evidence `.omo/evidence/telegram-button-checkin/task-1.md`.
  Commit: N | local profile state and source only

- [x] 2. Add a dormant, profile-gated Telegram wizard dispatch seam
  What to do / Must NOT do: Extend `/home/cube/projects/richard/hermes-agent/gateway/platforms/telegram.py` at its existing callback and textual ingress seams with a narrowly scoped `pc1:` handler that delegates to the profile-local wizard only when explicit `platforms.telegram.extra.physique_checkin` configuration enables it. The config must contain exact owner/chat/topic constraints, use `allowed_chats` plus `allowed_topics`, and use `require_mention: false` only for this solo topic. Intercept wizard typed values before generic agent/session aggregation. Verify callback sender, chat, topic thread, original message/session, monotonic state version, expiry, and action before mutation. Render numbered inline buttons and typed-input prompts using `message_thread_id`; answer callbacks and handle stale clicks. Payload grammar is `pc1:<opaque-128-bit-session-id>:<step>:<version>:<action>` within 64 bytes and contains no user data. Do not create a second getUpdates poller, accept callback data as personal values, log typed answers, or alter behavior for profiles without the feature flag.
  Parallelization: Wave 2 | Blocked by: 1 | Blocks: 3
  References (executor has NO interview context - be exhaustive): `gateway/platforms/telegram.py:2117,3393-3473,3939-4278,4144-4247,5875-5912,6675-6744`; `gateway/run.py:7228-7255`; existing `tests/gateway/test_telegram_clarify_buttons.py`; profile config `/home/cube/.hermes/profiles/physique-coach/config.yaml`.
  Acceptance criteria (agent-executable): callback data fits Telegram’s 64-byte cap and contains no values; adapter tests prove exact-topic delivery, authorized owner success even when generic group authorization is broader, foreign user/topic denial at text and callback ingress, duplicate/stale/expired callback safety, typed answer bypass of generic LLM/session aggregation, and disabled-profile no-op; existing Telegram callback tests remain green.
  QA scenarios (name the exact tool + invocation): happy: use the Telegram adapter test double to click `start`, select a bounded answer, then inject typed numeric input and assert the next same-topic prompt; failure: inject foreign-user/wrong-thread/replayed callback and assert a rejection with no state/event write. Evidence `.omo/evidence/telegram-button-checkin/task-2.md`.
  Commit: N | source change only, no default-profile config change

- [x] 3. Wire scheduled morning launch, workout entry, and profile activation configuration
  What to do / Must NOT do: Implement a narrow scheduler-to-existing-Telegram-adapter `send_inline_card(...)` capability with a fake-transport seam, because cron stdout delivery cannot attach a keyboard. Add profile config feature flag plus exact target constraints, update the existing `physique-morning-checkin` job in place from static text to an at-most-once daily button-card start/resume launch, and expose a topic-bound workout-entry action. Persist `launch_claimed` before sending; a post-claim send failure must be recoverable only by manual Start/Resume, never by duplicate automatic sends. Migrate both active profile cron script fields from `scripts/<name>` to resolver-valid `<name>` under the profile cron lock, validate before enabling, preserve job IDs/schedules, and retain a rollback copy until card tests pass. Ensure existing source refresh remains independent. Do not send test messages to non-target chats, start any extra gateway/poller, mutate the default Hermes cron store, or log secrets.
  Parallelization: Wave 3 | Blocked by: 1, 2 | Blocks: final wave
  References (executor has NO interview context - be exhaustive): profile `scripts/physique-checkin-gate.sh`, `.env`, `config.yaml`, `activation/cron-jobs.paused.yaml`; active profile cron job name `physique-morning-checkin`; `cron/scheduler.py:396-563,571-640,775-899`; `gateway/delivery.py:103-169,353-402`.
  Acceptance criteria (agent-executable): only one morning button card per KST day per owner/topic under before-send and after-send fault injection; restart/replay resumes rather than duplicates; direct scheduled destination resolves to the configured forum topic; disabled flag is silent; profile scripts resolve without errors; source job behavior unchanged; profile settings remain owner-only and default Hermes hash remains unchanged.
  QA scenarios (name the exact tool + invocation): happy: run the profile-local launcher in a fake Telegram transport and inspect the Start/Resume keyboard with target thread; failure: inject before-send and after-send failures, run it twice, with wrong home, and with feature disabled, asserting zero or exactly one queued launch as applicable. Evidence `.omo/evidence/telegram-button-checkin/task-3.md`.
  Commit: N | local profile state and source only

- [ ] 4. Perform profile-only gateway handoff and runtime acceptance checks
  What to do / Must NOT do: After code and fake-transport tests pass, restart only the profile gateway with the exact profile `HERMES_HOME`; never restart or interrupt the default gateway. Verify there is exactly one physical-profile poller and one untouched default poller. Send one user-authorized button-card acceptance message only to the configured physique topic, complete an isolated test draft through final save, and clean up the test draft/claim through explicit wizard test support. Do not expose token, raw values, IDs, or non-target messages in evidence.
  Parallelization: Wave 4 | Blocked by: 1, 2, 3 | Blocks: final wave
  References (executor has NO interview context - be exhaustive): active profile gateway process via `/proc/*/environ`, `gateway/platforms/telegram.py`, profile `config.yaml`, profile cron jobs, existing default gateway separation evidence.
  Acceptance criteria (agent-executable): profile-only restart succeeds; default gateway PID/HERMES_HOME remains unchanged; one target-topic card accepts an authorized Start callback and one typed value without reaching generic LLM/session logs; a foreign/wrong-topic probe is denied; exactly one final profile event is created after explicit save; credentials/values are redacted from evidence.
  QA scenarios (name the exact tool + invocation): happy: Telegram adapter/Bot API action against the user-authorized target topic plus profile process inspection; failure: controlled wrong-thread/foreign callback fixture and pre-save restart fixture. Evidence `.omo/evidence/telegram-button-checkin/task-4.md`.
  Commit: N | no commit; local service handoff only

## Final verification wave
> Runs in parallel after ALL todos. ALL must APPROVE. Surface results and wait for the user's explicit okay before declaring complete.
- [ ] F1. Plan compliance audit
- [ ] F2. Code quality review
- [ ] F3. Real manual QA
- [ ] F4. Scope fidelity

## Commit strategy

No git commit by default. The profile is user-owned local state; the Hermes-agent source tree may already contain unrelated user changes. Preserve and isolate unrelated changes.

## Success criteria

- The configured profile owner can complete or resume a compact morning check-in and separate workout record entirely from one Telegram forum topic using buttons plus typed values.
- Only final confirmation creates an append-only canonical event, and every duplicate, replayed, foreign, wrong-topic, invalid, or urgent path is safe.
- The active 08:11 KST job sends one Start/Resume card into the exact topic, while no default Hermes profile behavior or secret changes.
- Telegram callback payloads, logs, and external queues contain no user health/training values.
