# Todo 3 — Scheduled launcher and profile activation evidence

## Scope delivered

- Preserved the existing morning job ID and `11 8 * * *` schedule; converted it from stdout delivery to the profile-gated `physique-checkin-morning` inline-card path.
- Preserved the existing source-refresh job ID and hourly schedule.
- Corrected both cron script fields to resolver-valid basenames under the profile `scripts/` directory.
- Added exact owner/chat/topic configuration, matching single-target allowlists, and `require_mention: false` to the isolated profile only.
- Added one KST-day atomic claim before the existing Telegram adapter is asked to send its button card. A send failure retains the claim, so automatic retry cannot create a duplicate; user Start/Resume remains the recovery route.
- The card exposes both the morning check-in and post-workout entry actions in the configured Telegram topic.

## Automated checks

```text
/home/cube/projects/richard/hermes-agent/venv/bin/python -m pytest \
  tests/cron/test_physique_inline_card.py \
  tests/cron/test_cron_script.py \
  tests/gateway/test_telegram_physique_checkin.py -q
63 passed

bash /home/cube/.hermes/profiles/physique-coach/activation/tests/test_atomic_gates.sh
PASS

bash /home/cube/.hermes/profiles/physique-coach/activation/tests/test_active_cron_inline_card.sh
PASS
```

The scheduler tests cover one-card-per-KST-day, post-claim send failure without automatic retry, disabled silence/no claim, scheduler interception before stdout delivery, and gateway-loop fake transport delivery. Telegram adapter tests cover exact-topic keyboard sending, disabled no-op, opaque callbacks, the two launcher buttons, and manual `체크인 시작` recovery. A combined failure/recovery test proves: a post-claim scheduled failure remains claimed, the exact owner command schedules one recovery card, and a healthy later scheduled invocation is rejected as a duplicate. Recovery is consumed before generic LLM/session ingress, only sends for the exact owner/topic, and foreign or wrong-topic recovery commands are denied without an outbound message.

## Read-only profile checks

```text
profile inline-card config valid
profile scripts resolve silently
```

The profile cron validation held its existing `.jobs.lock` while checking the retained IDs, schedules, resolver paths, inline-card field, and independent source job. The atomic-gate test now handles the expected no-match `grep` result safely under `pipefail`. No gateway, poller, or live Telegram send was started. The default Hermes configuration was not edited.

## Safety notes

- The normal cron stdout delivery route is bypassed for the inline card and cannot attach or simulate buttons.
- No token, numeric identity, chat ID, or health/training value is included in this evidence.
- The profile contains a pre-change cron rollback manifest at `cron/jobs.before-inline-card.json`.
