tests/gateway/test_telegram_physique_checkin.py-291-        assert "1은 매우 나쁨" in prompts["condition"].text
--
tests/gateway/test_telegram_physique_checkin.py-296-            False,
tests/gateway/test_telegram_physique_checkin.py-297-            callback,
tests/gateway/test_telegram_physique_checkin.py:298:            flow="nutrition_daily",
tests/gateway/test_telegram_physique_checkin.py-299-        )
tests/gateway/test_telegram_physique_checkin.py-300-        note = build_wizard_prompt(
--
tests/gateway/test_telegram_physique_checkin.py-302-            False,
tests/gateway/test_telegram_physique_checkin.py-303-            callback,
tests/gateway/test_telegram_physique_checkin.py:304:            flow="nutrition_daily",
tests/gateway/test_telegram_physique_checkin.py-305-        )
tests/gateway/test_telegram_physique_checkin.py-306-        summary = build_wizard_prompt(
--
tests/gateway/test_telegram_physique_checkin.py-308-            False,
tests/gateway/test_telegram_physique_checkin.py-309-            callback,
tests/gateway/test_telegram_physique_checkin.py:310:            flow="nutrition_daily",
tests/gateway/test_telegram_physique_checkin.py-311-        )
tests/gateway/test_telegram_physique_checkin.py-312-
--
tests/gateway/test_telegram_physique_checkin.py-393-        assert menu.accepted is True
tests/gateway/test_telegram_physique_checkin.py-394-        assert menu.prompt is not None and menu.prompt.text == "어느 항목을 수정할까요?"
tests/gateway/test_telegram_physique_checkin.py:395:        assert binding.step == "sleep_quality"
tests/gateway/test_telegram_physique_checkin.py-396-        assert field.accepted is True
tests/gateway/test_telegram_physique_checkin.py-397-        assert service.answers == [("0123456789abcdef0123456789abcdef", 2, "edit", "bodyweight")]
--
tests/gateway/test_telegram_physique_checkin.py-502-        assert snapshot["answers"]["bodyweight"] is None
tests/gateway/test_telegram_physique_checkin.py-503-
tests/gateway/test_telegram_physique_checkin.py:504:    def test_model_can_rewrite_the_current_sleep_quality_prompt_without_advancing_the_draft(self, tmp_path):
tests/gateway/test_telegram_physique_checkin.py-505-        bridge, service = _bridge(tmp_path)
tests/gateway/test_telegram_physique_checkin.py-506-        launch = bridge.open_launcher("morning", message_id="44")
--
tests/gateway/test_telegram_physique_checkin.py-508-        bridge.handle_callback(launch.callback_data, "owner-1", "chat-1", "topic-1", "44")
tests/gateway/test_telegram_physique_checkin.py-509-        binding = bridge._bindings["0123456789abcdef0123456789abcdef"]
tests/gateway/test_telegram_physique_checkin.py:510:        binding.step = "sleep_quality"
tests/gateway/test_telegram_physique_checkin.py-511-        binding.version = 2
tests/gateway/test_telegram_physique_checkin.py-512-        binding.awaiting_text = False
--
tests/gateway/test_telegram_physique_checkin.py-595-            binding_path=binding_path,
tests/gateway/test_telegram_physique_checkin.py-596-        )
tests/gateway/test_telegram_physique_checkin.py:597:        launch = first.open_launcher("nutrition_daily", message_id="44")
tests/gateway/test_telegram_physique_checkin.py-598-        assert launch.callback_data is not None
tests/gateway/test_telegram_physique_checkin.py-599-        started = first.handle_callback(
--
tests/gateway/test_telegram_physique_checkin.py-652-        )
tests/gateway/test_telegram_physique_checkin.py-653-        resume_launch = restarted.open_launcher(
tests/gateway/test_telegram_physique_checkin.py:654:            "nutrition_daily",
tests/gateway/test_telegram_physique_checkin.py-655-            message_id="44",
tests/gateway/test_telegram_physique_checkin.py-656-        )
--
tests/gateway/test_telegram_physique_checkin.py-667-        assert resumed.prompt.text.startswith("진행 2/12")
tests/gateway/test_telegram_physique_checkin.py-668-
tests/gateway/test_telegram_physique_checkin.py:669:    def test_nutrition_navigation_preserves_sleep_quality_choices(
tests/gateway/test_telegram_physique_checkin.py-670-        self,
tests/gateway/test_telegram_physique_checkin.py-671-        tmp_path,
--
tests/gateway/test_telegram_physique_checkin.py-681-            binding_path=tmp_path / "bindings.json",
tests/gateway/test_telegram_physique_checkin.py-682-        )
tests/gateway/test_telegram_physique_checkin.py:683:        launch = bridge.open_launcher("nutrition_daily", message_id="44")
tests/gateway/test_telegram_physique_checkin.py-684-        assert launch.callback_data is not None
tests/gateway/test_telegram_physique_checkin.py-685-        reply = bridge.handle_callback(
--
tests/gateway/test_telegram_physique_checkin.py-721-            binding_path=tmp_path / "bindings.json",
tests/gateway/test_telegram_physique_checkin.py-722-        )
tests/gateway/test_telegram_physique_checkin.py:723:        launch = bridge.open_launcher("nutrition_daily", message_id="44")
tests/gateway/test_telegram_physique_checkin.py-724-        assert launch.callback_data is not None
tests/gateway/test_telegram_physique_checkin.py-725-        reply = bridge.handle_callback(
--
tests/gateway/test_telegram_physique_checkin.py-742-        }
tests/gateway/test_telegram_physique_checkin.py-743-        selected_labels = {
tests/gateway/test_telegram_physique_checkin.py:744:            "sleep_quality": "4",
tests/gateway/test_telegram_physique_checkin.py-745-            "digestion": "4 · 매끈하고 부드러움",
tests/gateway/test_telegram_physique_checkin.py-746-            "condition": "4",
--
tests/gateway/test_telegram_physique_checkin.py-755-            "water": (),
tests/gateway/test_telegram_physique_checkin.py-756-            "sleep_duration": (),
tests/gateway/test_telegram_physique_checkin.py:757:            "sleep_quality": ("a1", "a2", "a3", "a4", "a5"),
tests/gateway/test_telegram_physique_checkin.py-758-            "digestion": ("a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8"),
tests/gateway/test_telegram_physique_checkin.py-759-            "condition": ("a1", "a2", "a3", "a4", "a5"),
--
tests/gateway/test_telegram_physique_checkin.py-763-            "summary": ("a0", "a1", "a2"),
tests/gateway/test_telegram_physique_checkin.py-764-        }
tests/gateway/test_telegram_physique_checkin.py:765:        assert bridge._flow_steps("nutrition_daily") == tuple(expected_choice_actions)
tests/gateway/test_telegram_physique_checkin.py-766-        visited_steps = []
tests/gateway/test_telegram_physique_checkin.py-767-
--
tests/gateway/test_telegram_physique_checkin.py-848-            "water",
tests/gateway/test_telegram_physique_checkin.py-849-            "sleep_duration",
tests/gateway/test_telegram_physique_checkin.py:850:            "sleep_quality",
tests/gateway/test_telegram_physique_checkin.py-851-            "digestion",
tests/gateway/test_telegram_physique_checkin.py-852-            "condition",
--
tests/gateway/test_telegram_physique_checkin.py-882-        typed_answers = ("70.2", "7", "2400")
tests/gateway/test_telegram_physique_checkin.py-883-        button_answers = (
tests/gateway/test_telegram_physique_checkin.py:884:            ("sleep_quality", "a5"),
tests/gateway/test_telegram_physique_checkin.py-885-            ("condition", "a5"),
tests/gateway/test_telegram_physique_checkin.py-886-            ("pain", "a0"),
--
tests/gateway/test_telegram_physique_checkin.py-1128-            "water": "3",
tests/gateway/test_telegram_physique_checkin.py-1129-            "sleep_duration": "6",
tests/gateway/test_telegram_physique_checkin.py:1130:            "sleep_quality": "3",
tests/gateway/test_telegram_physique_checkin.py-1131-            "digestion": "bristol_5",
tests/gateway/test_telegram_physique_checkin.py-1132-            "condition": "5",
--
tests/gateway/test_telegram_physique_checkin.py-2179-
tests/gateway/test_telegram_physique_checkin.py-2180-    @pytest.mark.asyncio
tests/gateway/test_telegram_physique_checkin.py:2181:    async def test_anonymous_sleep_quality_feedback_rewrites_the_active_card_without_generic_ingress(self):
tests/gateway/test_telegram_physique_checkin.py-2182-        adapter = self._adapter({"physique_checkin": {
tests/gateway/test_telegram_physique_checkin.py-2183-            "enabled": True, "owner_id": "owner-1", "chat_id": "chat-1", "topic_id": "topic-1",
--
tests/gateway/test_telegram_physique_checkin.py-2187-        fake.handle_text.return_value = None
tests/gateway/test_telegram_physique_checkin.py-2188-        fake.active_checkin_snapshot.return_value = {
tests/gateway/test_telegram_physique_checkin.py:2189:            "step": "sleep_quality", "flow": "morning", "kst_day": "2026-07-18", "answers": {"sleep_duration": "7"},
tests/gateway/test_telegram_physique_checkin.py-2190-        }
tests/gateway/test_telegram_physique_checkin.py-2191-        prompt = WizardPrompt(
tests/gateway/test_telegram_physique_checkin.py-2192-            "수면 질 점수 (1=매우 나쁨 · 3=보통 · 5=매우 좋음)를 골라줘.",
tests/gateway/test_telegram_physique_checkin.py:2193:            (("1", "pc1:0123456789abcdef0123456789abcdef:sleep_quality:2:a1"),),
tests/gateway/test_telegram_physique_checkin.py-2194-        )
tests/gateway/test_telegram_physique_checkin.py-2195-        fake.apply_model_action.return_value = WizardReply(True, True, "", prompt, None)
--
tests/gateway/test_telegram_physique_checkin.py-2586-        rendered = query.edit_message_text.call_args.kwargs["text"]
tests/gateway/test_telegram_physique_checkin.py-2587-        assert "오늘 체크인 완료" in rendered
tests/gateway/test_telegram_physique_checkin.py:2588:        assert rendered == adapter._nutrition_daily_text(
tests/gateway/test_telegram_physique_checkin.py-2589-            bridge.finalized_coaching_snapshot(session_id)
tests/gateway/test_telegram_physique_checkin.py-2590-        )
--
tests/gateway/test_telegram_physique_checkin.py-2668-                "training_summary": "웨이트 70분 · RPE 7",
tests/gateway/test_telegram_physique_checkin.py-2669-                "sleep_duration": "7.3",
tests/gateway/test_telegram_physique_checkin.py:2670:                "sleep_quality": "4",
tests/gateway/test_telegram_physique_checkin.py-2671-                "condition": "4",
tests/gateway/test_telegram_physique_checkin.py-2672-                "digestion": "normal",
--
tests/gateway/test_telegram_physique_checkin.py-2679-        )
tests/gateway/test_telegram_physique_checkin.py-2680-
tests/gateway/test_telegram_physique_checkin.py:2681:        assert TelegramAdapter._nutrition_daily_text(snapshot, feedback) == (
tests/gateway/test_telegram_physique_checkin.py-2682-            "오늘 체크인 완료\n\n"
tests/gateway/test_telegram_physique_checkin.py-2683-            "- 체중: 78.6kg\n"
--
tests/gateway/test_telegram_physique_checkin.py-2697-
tests/gateway/test_telegram_physique_checkin.py-2698-    def test_daily_copy_fails_closed_when_facts_or_model_are_missing(self):
tests/gateway/test_telegram_physique_checkin.py:2699:        assert TelegramAdapter._nutrition_daily_text({"answers": {}}) == (
tests/gateway/test_telegram_physique_checkin.py-2700-            "오늘 체크인 완료\n\n"
tests/gateway/test_telegram_physique_checkin.py-2701-            "- 체중: 기록 없음\n"
--
tests/gateway/test_telegram_physique_checkin.py-3761-        query.edit_message_text.assert_awaited_once()
tests/gateway/test_telegram_physique_checkin.py-3762-        assert query.edit_message_text.await_args.kwargs["text"] == (
tests/gateway/test_telegram_physique_checkin.py:3763:            adapter._nutrition_daily_text(snapshot)
tests/gateway/test_telegram_physique_checkin.py-3764-        )
tests/gateway/test_telegram_physique_checkin.py-3765-
--
tests/gateway/test_telegram_physique_checkin.py-3806-        query.edit_message_text.assert_awaited_once()
tests/gateway/test_telegram_physique_checkin.py-3807-        rendered = query.edit_message_text.await_args.kwargs["text"]
tests/gateway/test_telegram_physique_checkin.py:3808:        assert rendered == adapter._nutrition_daily_text(snapshot)
tests/gateway/test_telegram_physique_checkin.py-3809-
tests/gateway/test_telegram_physique_checkin.py-3810-    def test_adaptive_publication_rechecks_revision_before_reservation(self):
--
gateway/platforms/telegram_room_bootstrap_activation.py-252-    ) -> None:
gateway/platforms/telegram_room_bootstrap_activation.py-253-        """Durably reserve the exact customer notice; provider I/O stays in gateway."""
gateway/platforms/telegram_room_bootstrap_activation.py:254:        from gateway.platforms.telegram_activation_notice import ActivationNoticeStore
gateway/platforms/telegram_room_bootstrap_activation.py-255-
gateway/platforms/telegram_room_bootstrap_activation.py-256-        admin, _ = self._profile_modules()
--
gateway/platforms/telegram_room_bootstrap_activation.py-288-                "activation notice authority is unavailable"
gateway/platforms/telegram_room_bootstrap_activation.py-289-            ) from exc
gateway/platforms/telegram_room_bootstrap_activation.py:290:        ActivationNoticeStore(self.profile_root).reserve(
gateway/platforms/telegram_room_bootstrap_activation.py-291-            customer_key=session.customer_key,
gateway/platforms/telegram_room_bootstrap_activation.py-292-            starts_on=session.customer_draft.starts_on,
--
gateway/platforms/telegram_activation_notice.py-26-
gateway/platforms/telegram_activation_notice.py-27-
gateway/platforms/telegram_activation_notice.py:28:class ActivationNoticeError(ValueError):
gateway/platforms/telegram_activation_notice.py-29-    """Activation notice state is malformed, stale, or unsafe."""
gateway/platforms/telegram_activation_notice.py-30-
gateway/platforms/telegram_activation_notice.py-31-
gateway/platforms/telegram_activation_notice.py-32-@dataclass(frozen=True, slots=True)
gateway/platforms/telegram_activation_notice.py:33:class ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-34-    reservation_id: str
gateway/platforms/telegram_activation_notice.py-35-    customer_key: str
--
gateway/platforms/telegram_activation_notice.py-46-
gateway/platforms/telegram_activation_notice.py-47-
gateway/platforms/telegram_activation_notice.py:48:class ActivationNoticeStore:
gateway/platforms/telegram_activation_notice.py-49-    """Append-only reservation ledger for one completion notice per authority."""
gateway/platforms/telegram_activation_notice.py-50-
--
gateway/platforms/telegram_activation_notice.py-52-        root = Path(profile_root)
gateway/platforms/telegram_activation_notice.py-53-        if root.is_symlink():
gateway/platforms/telegram_activation_notice.py:54:            raise ActivationNoticeError("profile root symlinks are not allowed")
gateway/platforms/telegram_activation_notice.py-55-        root.mkdir(parents=True, exist_ok=True, mode=0o700)
gateway/platforms/telegram_activation_notice.py-56-        self.path = root / "data" / "activation-completion-notices.jsonl"
--
gateway/platforms/telegram_activation_notice.py-62-        self.path.parent.chmod(0o700)
gateway/platforms/telegram_activation_notice.py-63-        if self.path.is_symlink() or self.lock_path.is_symlink():
gateway/platforms/telegram_activation_notice.py:64:            raise ActivationNoticeError("activation notice paths are unsafe")
gateway/platforms/telegram_activation_notice.py-65-        fd = os.open(self.lock_path, os.O_CREAT | os.O_RDWR, 0o600)
gateway/platforms/telegram_activation_notice.py-66-        try:
--
gateway/platforms/telegram_activation_notice.py-78-            destination = parse_customer_private_delivery_address(value)
gateway/platforms/telegram_activation_notice.py-79-        except AddressConfigurationError as exc:
gateway/platforms/telegram_activation_notice.py:80:            raise ActivationNoticeError(str(exc)) from exc
gateway/platforms/telegram_activation_notice.py-81-        return {
gateway/platforms/telegram_activation_notice.py-82-            "user_id": str(destination.user_id),
--
gateway/platforms/telegram_activation_notice.py-93-        destination: Mapping[str, object],
gateway/platforms/telegram_activation_notice.py-94-        authority_digest: str,
gateway/platforms/telegram_activation_notice.py:95:    ) -> ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-96-        destination_pin = self._validate_destination(destination)
gateway/platforms/telegram_activation_notice.py-97-        if not customer_key or len(customer_key) > 128:
gateway/platforms/telegram_activation_notice.py:98:            raise ActivationNoticeError("activation notice customer is invalid")
gateway/platforms/telegram_activation_notice.py-99-        try:
gateway/platforms/telegram_activation_notice.py-100-            from datetime import date, time
--
gateway/platforms/telegram_activation_notice.py-103-            time.fromisoformat(daily_time)
gateway/platforms/telegram_activation_notice.py-104-        except (TypeError, ValueError) as exc:
gateway/platforms/telegram_activation_notice.py:105:            raise ActivationNoticeError("activation notice schedule is invalid") from exc
gateway/platforms/telegram_activation_notice.py-106-        if len(authority_digest) != 64 or any(c not in "0123456789abcdef" for c in authority_digest):
gateway/platforms/telegram_activation_notice.py:107:            raise ActivationNoticeError("activation notice authority digest is invalid")
gateway/platforms/telegram_activation_notice.py-108-        body = ACTIVATION_COMPLETION_TEMPLATE.format(
gateway/platforms/telegram_activation_notice.py-109-            starts_on=starts_on,
--
gateway/platforms/telegram_activation_notice.py-126-            if latest is not None:
gateway/platforms/telegram_activation_notice.py-127-                return latest
gateway/platforms/telegram_activation_notice.py:128:            receipt = ActivationNoticeReceipt(
gateway/platforms/telegram_activation_notice.py-129-                reservation_id=reservation_id,
gateway/platforms/telegram_activation_notice.py-130-                customer_key=customer_key,
--
gateway/platforms/telegram_activation_notice.py-140-            return receipt
gateway/platforms/telegram_activation_notice.py-141-
gateway/platforms/telegram_activation_notice.py:142:    def mark_sending(self, receipt: ActivationNoticeReceipt) -> ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-143-        return self._transition(receipt, "prepared", "sending", provider_authority=True)
gateway/platforms/telegram_activation_notice.py-144-
gateway/platforms/telegram_activation_notice.py-145-    def mark_delivered(
gateway/platforms/telegram_activation_notice.py:146:        self, receipt: ActivationNoticeReceipt, *, provider_receipt: str
gateway/platforms/telegram_activation_notice.py:147:    ) -> ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-148-        if not provider_receipt or len(provider_receipt) > 128:
gateway/platforms/telegram_activation_notice.py:149:            raise ActivationNoticeError("activation notice provider receipt is invalid")
gateway/platforms/telegram_activation_notice.py-150-        return self._transition(
gateway/platforms/telegram_activation_notice.py-151-            receipt,
--
gateway/platforms/telegram_activation_notice.py-155-        )
gateway/platforms/telegram_activation_notice.py-156-
gateway/platforms/telegram_activation_notice.py:157:    def mark_audited(self, receipt: ActivationNoticeReceipt) -> ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-158-        return self._transition(receipt, "delivered", "sent_audited")
gateway/platforms/telegram_activation_notice.py-159-
gateway/platforms/telegram_activation_notice.py-160-    def mark_unknown(
gateway/platforms/telegram_activation_notice.py:161:        self, receipt: ActivationNoticeReceipt, *, reason: str
gateway/platforms/telegram_activation_notice.py:162:    ) -> ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-163-        if receipt.state not in {"prepared", "sending"}:
gateway/platforms/telegram_activation_notice.py:164:            raise ActivationNoticeError("activation notice is not pending")
gateway/platforms/telegram_activation_notice.py-165-        return self._transition(receipt, receipt.state, "unknown", reason=reason[:128])
gateway/platforms/telegram_activation_notice.py-166-
gateway/platforms/telegram_activation_notice.py-167-    def _transition(
gateway/platforms/telegram_activation_notice.py-168-        self,
gateway/platforms/telegram_activation_notice.py:169:        receipt: ActivationNoticeReceipt,
gateway/platforms/telegram_activation_notice.py-170-        expected: str,
gateway/platforms/telegram_activation_notice.py-171-        target: str,
gateway/platforms/telegram_activation_notice.py-172-        **changes: object,
gateway/platforms/telegram_activation_notice.py:173:    ) -> ActivationNoticeReceipt:
gateway/platforms/telegram_activation_notice.py-174-        with self._locked():
gateway/platforms/telegram_activation_notice.py-175-            current = self._latest_unlocked().get(receipt.reservation_id)
gateway/platforms/telegram_activation_notice.py-176-            if current is None or current.state != expected or current != replace(receipt, provider_authority=False):
gateway/platforms/telegram_activation_notice.py:177:                raise ActivationNoticeError(f"activation notice is not {expected}")
gateway/platforms/telegram_activation_notice.py-178-            updated = replace(
gateway/platforms/telegram_activation_notice.py-179-                current,
--
gateway/platforms/telegram_activation_notice.py-186-            return updated
gateway/platforms/telegram_activation_notice.py-187-
gateway/platforms/telegram_activation_notice.py:188:    def receipts(self) -> tuple[ActivationNoticeReceipt, ...]:
gateway/platforms/telegram_activation_notice.py-189-        with self._locked():
gateway/platforms/telegram_activation_notice.py-190-            return tuple(self._rows_unlocked())
gateway/platforms/telegram_activation_notice.py-191-
gateway/platforms/telegram_activation_notice.py:192:    def latest(self) -> tuple[ActivationNoticeReceipt, ...]:
gateway/platforms/telegram_activation_notice.py-193-        with self._locked():
gateway/platforms/telegram_activation_notice.py-194-            return tuple(self._latest_unlocked().values())
gateway/platforms/telegram_activation_notice.py-195-
gateway/platforms/telegram_activation_notice.py:196:    def _rows_unlocked(self) -> list[ActivationNoticeReceipt]:
gateway/platforms/telegram_activation_notice.py-197-        if not self.path.exists():
gateway/platforms/telegram_activation_notice.py-198-            return []
gateway/platforms/telegram_activation_notice.py-199-        if self.path.is_symlink() or not self.path.is_file():
gateway/platforms/telegram_activation_notice.py:200:            raise ActivationNoticeError("activation notice ledger is unsafe")
gateway/platforms/telegram_activation_notice.py:201:        rows: list[ActivationNoticeReceipt] = []
gateway/platforms/telegram_activation_notice.py-202-        try:
gateway/platforms/telegram_activation_notice.py-203-            for line in self.path.read_text(encoding="utf-8").splitlines():
--
gateway/platforms/telegram_activation_notice.py-205-                if not isinstance(raw, dict):
gateway/platforms/telegram_activation_notice.py-206-                    raise ValueError
gateway/platforms/telegram_activation_notice.py:207:                rows.append(ActivationNoticeReceipt(**raw))
gateway/platforms/telegram_activation_notice.py-208-        except (OSError, TypeError, ValueError, json.JSONDecodeError) as exc:
gateway/platforms/telegram_activation_notice.py:209:            raise ActivationNoticeError("activation notice ledger is corrupt") from exc
gateway/platforms/telegram_activation_notice.py-210-        return rows
gateway/platforms/telegram_activation_notice.py-211-
gateway/platforms/telegram_activation_notice.py:212:    def _latest_unlocked(self) -> dict[str, ActivationNoticeReceipt]:
gateway/platforms/telegram_activation_notice.py:213:        latest: dict[str, ActivationNoticeReceipt] = {}
gateway/platforms/telegram_activation_notice.py-214-        for row in self._rows_unlocked():
gateway/platforms/telegram_activation_notice.py-215-            latest[row.reservation_id] = row
gateway/platforms/telegram_activation_notice.py-216-        return latest
gateway/platforms/telegram_activation_notice.py-217-
gateway/platforms/telegram_activation_notice.py:218:    def _append_unlocked(self, receipt: ActivationNoticeReceipt) -> None:
gateway/platforms/telegram_activation_notice.py-219-        payload = asdict(replace(receipt, provider_authority=False))
gateway/platforms/telegram_activation_notice.py-220-        with self.path.open("a", encoding="utf-8") as ledger:
--
gateway/platforms/telegram_activation_notice.py-227-__all__ = [
gateway/platforms/telegram_activation_notice.py-228-    "ACTIVATION_COMPLETION_TEMPLATE",
gateway/platforms/telegram_activation_notice.py:229:    "ActivationNoticeError",
gateway/platforms/telegram_activation_notice.py:230:    "ActivationNoticeReceipt",
gateway/platforms/telegram_activation_notice.py:231:    "ActivationNoticeStore",
gateway/platforms/telegram_activation_notice.py-232-]
--
gateway/platforms/korean_humanizer.py-220-            raise TypeError("finalized snapshot answers are invalid")
gateway/platforms/korean_humanizer.py-221-        known = {
gateway/platforms/korean_humanizer.py:222:            "bodyweight", "sleep_duration", "sleep_quality", "condition", "pain",
gateway/platforms/korean_humanizer.py-223-            "digestion", "calories", "training_plan", "completion", "training_summary",
gateway/platforms/korean_humanizer.py-224-            "workout_quality", "macros", "performance", "intensity",
--
scripts/whatsapp-bridge/package-lock.json-90-      "version": "1.1.0",
scripts/whatsapp-bridge/package-lock.json-91-      "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
scripts/whatsapp-bridge/package-lock.json:92:      "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
scripts/whatsapp-bridge/package-lock.json-93-      "license": "MIT",
scripts/whatsapp-bridge/package-lock.json-94-      "peer": true,
--
scripts/whatsapp-bridge/package-lock.json-787-      "version": "0.5.0",
scripts/whatsapp-bridge/package-lock.json-788-      "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
scripts/whatsapp-bridge/package-lock.json:789:      "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
scripts/whatsapp-bridge/package-lock.json-790-      "license": "MIT",
scripts/whatsapp-bridge/package-lock.json-791-      "dependencies": {
--
scripts/whatsapp-bridge/package-lock.json-1055-      "version": "4.22.1",
scripts/whatsapp-bridge/package-lock.json-1056-      "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
scripts/whatsapp-bridge/package-lock.json:1057:      "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
scripts/whatsapp-bridge/package-lock.json-1058-      "license": "MIT",
scripts/whatsapp-bridge/package-lock.json-1059-      "dependencies": {
--
scripts/whatsapp-bridge/package-lock.json-1146-      "version": "0.5.2",
scripts/whatsapp-bridge/package-lock.json-1147-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
scripts/whatsapp-bridge/package-lock.json:1148:      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
scripts/whatsapp-bridge/package-lock.json-1149-      "license": "MIT",
scripts/whatsapp-bridge/package-lock.json-1150-      "engines": {
--
scripts/whatsapp-bridge/package-lock.json-1255-      "version": "2.0.1",
scripts/whatsapp-bridge/package-lock.json-1256-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
scripts/whatsapp-bridge/package-lock.json:1257:      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
scripts/whatsapp-bridge/package-lock.json-1258-      "license": "MIT",
scripts/whatsapp-bridge/package-lock.json-1259-      "dependencies": {
--
scripts/whatsapp-bridge/package-lock.json-1365-      "version": "1.1.0",
scripts/whatsapp-bridge/package-lock.json-1366-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
scripts/whatsapp-bridge/package-lock.json:1367:      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
scripts/whatsapp-bridge/package-lock.json-1368-      "license": "MIT",
scripts/whatsapp-bridge/package-lock.json-1369-      "engines": {
--
gateway/platforms/telegram_room_bootstrap_cutover.py-295-            )
gateway/platforms/telegram_room_bootstrap_cutover.py-296-            # Prove runtime initialization can succeed before retiring the current
gateway/platforms/telegram_room_bootstrap_cutover.py:297:            # customer. activate_customer repeats this idempotently under the same
gateway/platforms/telegram_room_bootstrap_cutover.py-298-            # canonical profile lock.
gateway/platforms/telegram_room_bootstrap_cutover.py-299-            admin.initialize_adaptive_customer(requested_data)
--
gateway/platforms/telegram_room_bootstrap_cutover.py-317-            )
gateway/platforms/telegram_room_bootstrap_cutover.py-318-        if plan.phase == "current_disabled":
gateway/platforms/telegram_room_bootstrap_cutover.py:319:            admin.activate_customer(
gateway/platforms/telegram_room_bootstrap_cutover.py-320-                self.profile_root,
gateway/platforms/telegram_room_bootstrap_cutover.py-321-                requested_data,
--
gateway/platforms/nutrition_coaching_judgment_request.py-26-    "sleep_duration": "수면 시간",
gateway/platforms/nutrition_coaching_judgment_request.py-27-    "sleep_hours": "수면 시간",
gateway/platforms/nutrition_coaching_judgment_request.py:28:    "sleep_quality": "수면 질",
gateway/platforms/nutrition_coaching_judgment_request.py-29-    "training_summary": "운동",
gateway/platforms/nutrition_coaching_judgment_request.py-30-    "water": "수분",
--
gateway/platforms/physique_checkin.py-33-}
gateway/platforms/physique_checkin.py-34-_STEP: Final[re.Pattern[str]] = re.compile(
gateway/platforms/physique_checkin.py:35:    r"^(?:launch|done|bodyweight|sleep_duration|sleep_quality|condition|pain|calories|macros|"
gateway/platforms/physique_checkin.py-36-    r"meals|water|digestion|appetite_stress|training_plan|weight_change_percent|fatigue|"
gateway/platforms/physique_checkin.py-37-    r"exercise_feasibility|meal_deviation|wc|ft|ef|md|optional_note|summary|edit_menu|"
--
gateway/platforms/physique_checkin.py-181-        if flow == "morning":
gateway/platforms/physique_checkin.py-182-            result = self._service.start_morning(context, day)
gateway/platforms/physique_checkin.py:183:        elif flow == "nutrition_daily":
gateway/platforms/physique_checkin.py-184-            result = self._service.start_nutrition(context, day)
gateway/platforms/physique_checkin.py-185-        elif flow == "trainer_session":
--
gateway/platforms/physique_checkin.py-350-            return self._advance(binding, *navigation)
gateway/platforms/physique_checkin.py-351-        flow = self._session_flow(binding.session_id)
gateway/platforms/physique_checkin.py:352:        back_action = "e12" if flow == "nutrition_daily" else "e5" if flow in {"trainer_session", "trainer"} else "e11"
gateway/platforms/physique_checkin.py-353-        if parsed.step == "edit_menu" and parsed.action == back_action:
gateway/platforms/physique_checkin.py-354-            binding.step = "summary"
--
gateway/platforms/physique_checkin.py-547-            return None
gateway/platforms/physique_checkin.py-548-        allowed = {
gateway/platforms/physique_checkin.py:549:            "bodyweight", "sleep_duration", "sleep_quality", "condition", "pain",
gateway/platforms/physique_checkin.py-550-            "calories", "training_plan", "optional_note", "completion",
gateway/platforms/physique_checkin.py-551-            "training_summary", "workout_quality", "macros", "meals", "water",
--
gateway/platforms/physique_checkin.py-603-            return None
gateway/platforms/physique_checkin.py-604-        allowed = {
gateway/platforms/physique_checkin.py:605:            "bodyweight", "sleep_duration", "sleep_quality", "condition",
gateway/platforms/physique_checkin.py-606-            "pain", "calories", "training_plan", "optional_note",
gateway/platforms/physique_checkin.py-607-            "completion", "training_summary", "workout_quality",
--
gateway/platforms/physique_checkin.py-679-            elif flow in {"trainer_session", "trainer"}:
gateway/platforms/physique_checkin.py-680-                prompt = self._trainer_prompt(binding)
gateway/platforms/physique_checkin.py:681:            elif flow == "nutrition_daily" and binding.step == "summary":
gateway/platforms/physique_checkin.py-682-                prompt = self._nutrition_summary_prompt(binding)
gateway/platforms/physique_checkin.py-683-            else:
--
gateway/platforms/physique_checkin.py-738-            "bodyweight",
gateway/platforms/physique_checkin.py-739-            "sleep_duration",
gateway/platforms/physique_checkin.py:740:            "sleep_quality",
gateway/platforms/physique_checkin.py-741-            "condition",
gateway/platforms/physique_checkin.py-742-            "pain",
--
gateway/platforms/physique_checkin.py-757-        steps = {
gateway/platforms/physique_checkin.py-758-            "morning": morning,
gateway/platforms/physique_checkin.py:759:            "nutrition_daily": (
gateway/platforms/physique_checkin.py-760-                "bodyweight",
gateway/platforms/physique_checkin.py-761-                "calories",
--
gateway/platforms/physique_checkin.py-764-                "water",
gateway/platforms/physique_checkin.py-765-                "sleep_duration",
gateway/platforms/physique_checkin.py:766:                "sleep_quality",
gateway/platforms/physique_checkin.py-767-                "digestion",
gateway/platforms/physique_checkin.py-768-                "condition",
--
gateway/platforms/physique_checkin.py-816-            f"식사: {answers.get('meals', '-')}",
gateway/platforms/physique_checkin.py-817-            f"수분: {answers.get('water', '-')}L",
gateway/platforms/physique_checkin.py:818:            f"수면: {answers.get('sleep_duration', '-')}시간 / 질 {answers.get('sleep_quality', '-')}/5",
gateway/platforms/physique_checkin.py-819-            f"배변·소화: {digestion or '-'}",
gateway/platforms/physique_checkin.py-820-            f"컨디션: {answers.get('condition', '-')}/5",
--
gateway/platforms/physique_checkin.py-1002-            done = {"a0": "complete", "a1": "partial", "a2": "rest_changed"}
gateway/platforms/physique_checkin.py-1003-            return ("select", done[action], False) if action in done else None
gateway/platforms/physique_checkin.py:1004:        if step in {"sleep_quality", "condition", "workout_quality"} and action in {"a1", "a2", "a3", "a4", "a5"}:
gateway/platforms/physique_checkin.py-1005-            return "select", action[1:], False
gateway/platforms/physique_checkin.py-1006-        if flow in {"trainer_session", "trainer"} and step == "performance" and action in {"a1", "a2", "a3", "a4", "a5"}:
--
gateway/platforms/physique_checkin.py-1035-        if step == "completion":
gateway/platforms/physique_checkin.py-1036-            return ("select", ("complete", "partial", "rest_changed")[int(action[1:])], False) if action in {"a0", "a1", "a2"} else None
gateway/platforms/physique_checkin.py:1037:        if flow == "nutrition_daily" and step == "training_summary":
gateway/platforms/physique_checkin.py-1038-            if action == "a0":
gateway/platforms/physique_checkin.py-1039-                return "select", "trainer_recorded", False
--
gateway/platforms/physique_checkin.py-1044-            return "save", None, False
gateway/platforms/physique_checkin.py-1045-        editable = {
gateway/platforms/physique_checkin.py:1046:            "morning": ("bodyweight", "sleep_duration", "sleep_quality", "condition", "pain", "calories", "training_plan", "optional_note"),
gateway/platforms/physique_checkin.py-1047-            "workout": ("completion", "training_summary", "workout_quality", "pain"),
gateway/platforms/physique_checkin.py:1048:            "nutrition_daily": (
gateway/platforms/physique_checkin.py-1049-                "bodyweight", "calories", "macros", "meals", "water", "sleep_duration",
gateway/platforms/physique_checkin.py:1050:                "sleep_quality", "digestion", "condition", "appetite_stress",
gateway/platforms/physique_checkin.py-1051-                "training_summary", "optional_note",
gateway/platforms/physique_checkin.py-1052-            ),
--
gateway/platforms/physique_checkin.py-1082-        }:
gateway/platforms/physique_checkin.py-1083-            return action == "value" and value is not None
gateway/platforms/physique_checkin.py:1084:        if step in {"sleep_quality", "condition", "workout_quality", "completion", "done", "performance"}:
gateway/platforms/physique_checkin.py-1085-            return action == "select" and value is not None
gateway/platforms/physique_checkin.py-1086-        if step == "intensity":
--
gateway/platforms/physique_checkin_prompts.py-10-        ("체중", "bodyweight"),
gateway/platforms/physique_checkin_prompts.py-11-        ("수면 시간", "sleep_duration"),
gateway/platforms/physique_checkin_prompts.py:12:        ("수면 질", "sleep_quality"),
gateway/platforms/physique_checkin_prompts.py-13-        ("컨디션", "condition"),
gateway/platforms/physique_checkin_prompts.py-14-        ("통증", "pain"),
--
gateway/platforms/physique_checkin_prompts.py-23-        ("통증", "pain"),
gateway/platforms/physique_checkin_prompts.py-24-    ),
gateway/platforms/physique_checkin_prompts.py:25:    "nutrition_daily": (
gateway/platforms/physique_checkin_prompts.py-26-        ("체중", "bodyweight"),
gateway/platforms/physique_checkin_prompts.py-27-        ("칼로리", "calories"),
--
gateway/platforms/physique_checkin_prompts.py-30-        ("수분", "water"),
gateway/platforms/physique_checkin_prompts.py-31-        ("수면 시간", "sleep_duration"),
gateway/platforms/physique_checkin_prompts.py:32:        ("수면 질", "sleep_quality"),
gateway/platforms/physique_checkin_prompts.py-33-        ("소화", "digestion"),
gateway/platforms/physique_checkin_prompts.py-34-        ("컨디션", "condition"),
--
gateway/platforms/physique_checkin_prompts.py-63-    if step == "sleep_duration":
gateway/platforms/physique_checkin_prompts.py-64-        return WizardPrompt("어제 수면 시간을 시간 단위 숫자로 입력해 주세요. 예: 7.5")
gateway/platforms/physique_checkin_prompts.py:65:    if step in {"sleep_quality", "condition", "workout_quality"}:
gateway/platforms/physique_checkin_prompts.py-66-        labels = {
gateway/platforms/physique_checkin_prompts.py:67:            "sleep_quality": (
gateway/platforms/physique_checkin_prompts.py-68-                "어제 수면의 질을 골라 주세요.\n"
gateway/platforms/physique_checkin_prompts.py-69-                "1은 매우 나쁨, 3은 보통, 5는 매우 좋음입니다."
--
gateway/platforms/physique_checkin_prompts.py-101-        buttons = (
gateway/platforms/physique_checkin_prompts.py-102-            (("없음", callback("a0")), ("직접 입력", callback("a1")))
gateway/platforms/physique_checkin_prompts.py:103:            if flow == "nutrition_daily"
gateway/platforms/physique_checkin_prompts.py-104-            else (("없음 · 계속", callback("a0")), ("입력", callback("a1")))
gateway/platforms/physique_checkin_prompts.py-105-        )
gateway/platforms/physique_checkin_prompts.py-106-        return WizardPrompt("특이사항이 있으면 입력해 주세요.", buttons)
gateway/platforms/physique_checkin_prompts.py-107-    if step == "completion": return WizardPrompt("계획 대비 훈련 결과를 골라주세요.", (("완료", callback("a0")), ("부분 완료", callback("a1")), ("휴식으로 변경", callback("a2"))))
gateway/platforms/physique_checkin_prompts.py:108:    if step == "training_summary" and flow == "nutrition_daily":
gateway/platforms/physique_checkin_prompts.py-109-        return WizardPrompt(
gateway/platforms/physique_checkin_prompts.py-110-            "어제 트레이너가 운동 기록을 남겼다면 저장된 기록을 사용할 수 있습니다.",
--
gateway/platforms/physique_checkin_prompts.py-123-                ("나중에 저장", callback("a2")),
gateway/platforms/physique_checkin_prompts.py-124-            )
gateway/platforms/physique_checkin_prompts.py:125:            if flow == "nutrition_daily"
gateway/platforms/physique_checkin_prompts.py-126-            else (
gateway/platforms/physique_checkin_prompts.py-127-                ("저장", callback("a0")),
--
gateway/platforms/physique_checkin_prompts.py-133-            "오늘 체크인 내용을 확인해 주세요.\n"
gateway/platforms/physique_checkin_prompts.py-134-            "‘체크인 저장’을 누르면 오늘 기록을 제출합니다."
gateway/platforms/physique_checkin_prompts.py:135:            if flow == "nutrition_daily"
gateway/platforms/physique_checkin_prompts.py-136-            else "오늘 체크인을 저장할까요?"
gateway/platforms/physique_checkin_prompts.py-137-        )
--
gateway/platforms/physique_checkin_prompts.py-141-        buttons = tuple((label, callback(f"e{index}")) for index, (label, _) in enumerate(fields))
gateway/platforms/physique_checkin_prompts.py-142-        rows = tuple(buttons[index:index + 2] for index in range(0, len(buttons), 2))
gateway/platforms/physique_checkin_prompts.py:143:        back = ("요약으로 돌아가기", callback("e12" if flow == "nutrition_daily" else "e11"))
gateway/platforms/physique_checkin_prompts.py-144-        return WizardPrompt("어느 항목을 수정할까요?", buttons + (back,), rows + ((back,),))
gateway/platforms/physique_checkin_prompts.py-145-    return WizardPrompt("체크인을 다시 시작해 주세요.")
--
scripts/nutrition-room-bootstrap-325-
scripts/nutrition-room-bootstrap-326-    cutover = commands.add_parser(
scripts/nutrition-room-bootstrap:327:        "activate-cutover",
scripts/nutrition-room-bootstrap-328-        help="preflight or execute one explicit canonical customer cutover",
scripts/nutrition-room-bootstrap-329-    )
--
scripts/nutrition-room-bootstrap-449-            attempt_generation=args.attempt_generation,
scripts/nutrition-room-bootstrap-450-        ))
scripts/nutrition-room-bootstrap:451:    elif args.command == "activate-cutover":
scripts/nutrition-room-bootstrap-452-        active_store = _require_store(store)
scripts/nutrition-room-bootstrap-453-        cutover = TelegramRoomBootstrapCutover(
--
gateway/platforms/nutrition_coaching.py-2792-        if resolved is None:
gateway/platforms/nutrition_coaching.py-2793-            return WizardReply(True, False, "고객 경로를 사용할 수 없습니다.", None, None)
gateway/platforms/nutrition_coaching.py:2794:        opening = resolved.bridge.open_launcher("nutrition_daily")
gateway/platforms/nutrition_coaching.py-2795-        if not opening.accepted:
gateway/platforms/nutrition_coaching.py-2796-            return opening
--
gateway/platforms/telegram.py-6641-            ("water", "수분"),
gateway/platforms/telegram.py-6642-            ("sleep_duration", "수면 시간"),
gateway/platforms/telegram.py:6643:            ("sleep_quality", "수면 질"),
gateway/platforms/telegram.py-6644-            ("digestion", "소화·배변"),
gateway/platforms/telegram.py-6645-            ("condition", "컨디션"),
--
gateway/platforms/telegram.py-6727-            "fat_g": "지방",
gateway/platforms/telegram.py-6728-            "sleep_hours": "수면 시간",
gateway/platforms/telegram.py:6729:            "sleep_quality_1to5": "수면 질",
gateway/platforms/telegram.py-6730-            "readiness_1to5": "컨디션",
gateway/platforms/telegram.py-6731-            "training_summary": "운동",
--
gateway/platforms/telegram.py-7681-                # Telegram callbacks render only the finalized authority snapshot.
gateway/platforms/telegram.py-7682-                # Provider-backed coaching belongs to the out-of-band worker seam.
gateway/platforms/telegram.py:7683:                text = self._nutrition_daily_text(snapshot)
gateway/platforms/telegram.py-7684-        try:
gateway/platforms/telegram.py-7685-            await query.edit_message_text(
--
gateway/platforms/telegram.py-7692-            return
gateway/platforms/telegram.py-7693-    @staticmethod
gateway/platforms/telegram.py:7694:    def _nutrition_daily_text(
gateway/platforms/telegram.py-7695-        snapshot: object,
gateway/platforms/telegram.py-7696-        feedback: object | None = None,
--
gateway/platforms/telegram.py-7698-        """Render the approved daily check-in copy from finalized facts."""
gateway/platforms/telegram.py-7699-        answers = TelegramAdapter._nutrition_snapshot_answers(snapshot)
gateway/platforms/telegram.py:7700:        facts = TelegramAdapter._nutrition_daily_facts(answers)
gateway/platforms/telegram.py-7701-        missing = any(value.endswith("기록 없음") for value in facts)
gateway/platforms/telegram.py:7702:        interpretation = TelegramAdapter._nutrition_daily_interpretation(feedback)
gateway/platforms/telegram.py:7703:        actions = TelegramAdapter._nutrition_daily_actions(answers, missing)
gateway/platforms/telegram.py-7704-        lines = [
gateway/platforms/telegram.py-7705-            "오늘 체크인 완료",
--
gateway/platforms/telegram.py-7859-
gateway/platforms/telegram.py-7860-    @staticmethod
gateway/platforms/telegram.py:7861:    def _nutrition_daily_facts(answers: Mapping[str, object]) -> tuple[str, ...]:
gateway/platforms/telegram.py-7862-        bodyweight = TelegramAdapter._nutrition_snapshot_value(answers, "bodyweight")
gateway/platforms/telegram.py-7863-        calories = TelegramAdapter._nutrition_snapshot_value(answers, "calories")
gateway/platforms/telegram.py-7864-        macros = answers.get("macros")
gateway/platforms/telegram.py-7865-        sleep_duration = TelegramAdapter._nutrition_snapshot_value(answers, "sleep_duration")
gateway/platforms/telegram.py:7866:        sleep_quality = TelegramAdapter._nutrition_snapshot_value(answers, "sleep_quality")
gateway/platforms/telegram.py-7867-        condition = TelegramAdapter._nutrition_snapshot_value(answers, "condition")
gateway/platforms/telegram.py-7868-        digestion = TelegramAdapter._nutrition_snapshot_value(answers, "digestion")
--
gateway/platforms/telegram.py-7883-                sleep_duration, "시간", digits=2
gateway/platforms/telegram.py-7884-            )
gateway/platforms/telegram.py:7885:            if sleep_quality:
gateway/platforms/telegram.py:7886:                sleep_text += f" · 수면 질 {TelegramAdapter._nutrition_scaled_value(sleep_quality)}"
gateway/platforms/telegram.py-7887-        else:
gateway/platforms/telegram.py-7888-            sleep_text = "기록 없음"
--
gateway/platforms/telegram.py-7907-
gateway/platforms/telegram.py-7908-    @staticmethod
gateway/platforms/telegram.py:7909:    def _nutrition_daily_interpretation(feedback: object | None) -> str:
gateway/platforms/telegram.py-7910-        fallback = "일부 항목이 기록되지 않아 저장된 내용만 안내합니다."
gateway/platforms/telegram.py-7911-        lines = []
--
gateway/platforms/telegram.py-7933-
gateway/platforms/telegram.py-7934-    @staticmethod
gateway/platforms/telegram.py:7935:    def _nutrition_daily_actions(
gateway/platforms/telegram.py-7936-        answers: Mapping[str, object],
gateway/platforms/telegram.py-7937-        missing: bool,
--
gateway/platforms/telegram.py-8127-    async def _render_physique_feedback_replay(self, message: object, snapshot: dict[str, object]) -> None:
gateway/platforms/telegram.py-8128-        """Render only the canonical replay after a current-authority check."""
gateway/platforms/telegram.py:8129:        canonical = self._nutrition_daily_text(snapshot)
gateway/platforms/telegram.py-8130-        bridge = self._get_physique_checkin()
gateway/platforms/telegram.py-8131-        if bridge is None:
--
gateway/platforms/telegram.py-8146-            supplier = lambda: self._daily_grounding_input(
gateway/platforms/telegram.py-8147-                bridge.latest_finalized_coaching_snapshot(),
gateway/platforms/telegram.py:8148:                canonical=self._nutrition_daily_text(
gateway/platforms/telegram.py-8149-                    bridge.latest_finalized_coaching_snapshot()
gateway/platforms/telegram.py-8150-                ),
--
gateway/platforms/telegram.py-8406-                "bodyweight",
gateway/platforms/telegram.py-8407-                "sleep_duration",
gateway/platforms/telegram.py:8408:                "sleep_quality",
gateway/platforms/telegram.py-8409-                "condition",
gateway/platforms/telegram.py-8410-                "pain",
--
gateway/platforms/telegram.py-9556-
gateway/platforms/telegram.py-9557-    @staticmethod
gateway/platforms/telegram.py:9558:    def _nutrition_daily_task_is_current(
gateway/platforms/telegram.py-9559-        task: object,
gateway/platforms/telegram.py-9560-        customer: object,
--
gateway/platforms/telegram.py-9624-    ) -> tuple[str, ...]:
gateway/platforms/telegram.py-9625-        """Drain reserved activation notices with provider-at-most-once semantics."""
gateway/platforms/telegram.py:9626:        from gateway.platforms.telegram_activation_notice import ActivationNoticeStore
gateway/platforms/telegram.py-9627-
gateway/platforms/telegram.py:9628:        store = ActivationNoticeStore(_Path(str(profile_root)))
gateway/platforms/telegram.py-9629-        failures: list[str] = []
gateway/platforms/telegram.py-9630-        for receipt in store.latest():
--
gateway/platforms/telegram.py-9851-                failures.append(task_key)
gateway/platforms/telegram.py-9852-                continue
gateway/platforms/telegram.py:9853:            if task.kind == "daily" and not self._nutrition_daily_task_is_current(
gateway/platforms/telegram.py-9854-                task,
gateway/platforms/telegram.py-9855-                customer,
--
gateway/platforms/telegram.py-10037-                    (
gateway/platforms/telegram.py-10038-                        task.kind == "daily"
gateway/platforms/telegram.py:10039:                        and not self._nutrition_daily_task_is_current(
gateway/platforms/telegram.py-10040-                            task,
gateway/platforms/telegram.py-10041-                            current_customer,

[Showing lines 148-782 of 782 (50.0KB limit). Full output: /tmp/pi-bash-c3dc083112494c49.log]


Recent evidence candidates and active-seal references:
1786529321.4949946670 3703 .omo/evidence/dualcoach-task-22-evidence-index.json
1786529293.1015089430 4858 .omo/evidence/dualcoach-task-22.md
1786527840.1292440510 2956 .omo/evidence/task22-supplemental-compatibility-canonical.py
1786527821.4522584310 1001 .omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json
1786527821.4482584350 1170 .omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json
1786527821.4422584400 186348 .omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json
1786527818.9042606530 8476988 .omo/evidence/dualcoach-task22-supplemental-compatibility.whl
1786526588.6118598750 15973 .omo/evidence/verify-task22-supplemental-candidate.py
1786526577.7177694740 12414 .omo/evidence/reseal-task22-supplemental-candidate.py
1786518809.4310244690 1166 .omo/evidence/dualcoach-task22-supplemental-successor-wheel.json
1786518809.4100243220 190427 .omo/evidence/dualcoach-task22-supplemental-successor-manifest.json
1786518779.1938122970 8537518 .omo/evidence/dualcoach-task22-supplemental-successor.whl
1786511771.5312864760 11069 .omo/evidence/task22-successor-gateway-wheel-debug-journal.md
1786511771.5312348180 4233 .omo/evidence/dualcoach-trainer-removal-successor-manifest.md
1786511771.5311966460 23856 .omo/evidence/task22-trainer-authority-removal-canonical.py
1786511771.5311384960 14023 .omo/evidence/verify-dualcoach-trainer-removal-candidate.py
1786511771.5310696960 4051 .omo/evidence/dualcoach-trainer-removal-successor-freeze-receipt.json
1786511771.5308289100 189073 .omo/evidence/dualcoach-trainer-removal-successor-manifest.json
1786488929.4478175860 8013 .omo/evidence/test_task22_pidfd_timeout.py
1786488929.4468175740 6321 .omo/evidence/test_task22_parent_death_lifecycle.py
1786487705.5835976070 10402 .omo/evidence/test_task22_sealed_module_identity.py
1786486353.5102009930 16638 .omo/evidence/test_task22_trainer_removal_launcher_hardening.py
1786475525.1053059980 12502 .omo/evidence/task22_launcher_test_support.py
1786473562.9083438650 49946 .omo/evidence/dualcoach-callback-adoption-successor-manifest.md
1786473479.6005491630 19099 .omo/evidence/verify-dualcoach-callback-adoption-candidate.py
1786473416.4339569190 3529 .omo/evidence/dualcoach-callback-adoption-successor-freeze-receipt.json
1786473373.6325613320 184356 .omo/evidence/dualcoach-callback-adoption-successor-manifest.json
1786472863.4627754860 10112 .omo/evidence/task22_lifecycle.py
1786472863.4614398030 8635 .omo/evidence/task22_child_protocol.py
1786472454.9132463870 9732 .omo/evidence/task22_launcher_runtime.py
1786470706.5559214080 6370 .omo/evidence/task22_resource_ownership.py
1786460134.2885403130 8072 .omo/evidence/test_task22_pidfd_contract.py
1786458318.3215762410 6269 .omo/evidence/test_task22_launcher_complete_closure.py
1786457962.7750651860 7595 .omo/evidence/task22_dependency_closure.py
1786455896.4294833630 3383 .omo/evidence/test_task22_launcher_venv_fork.py
1786442752.7653502980 35861 .omo/evidence/dualcoach-task-22-pre-callback-adoption-evidence.json
1786436084.1188609260 2155876 .omo/evidence/dualcoach-task22-protected-session-preflight-blocker.json
1786415797.1881717100 30910 .omo/evidence/dualcoach-callback-cleanup-successor-manifest.md
1786415797.1879707610 3321 .omo/evidence/dualcoach-task-22-pre-callback-cleanup-evidence.json
1786415797.1840275720 2564 .omo/evidence/dualcoach-callback-cleanup-successor-freeze-receipt.json
1786415797.1838130660 152339 .omo/evidence/dualcoach-callback-cleanup-successor-manifest.json
1786395090.9553109620 31851 .omo/evidence/dualcoach-candidate-manifest.md
1786395090.9542756670 5607 .omo/evidence/dualcoach-task-22-pre-supplemental-evidence.json
1786395090.9506223640 2439 .omo/evidence/dualcoach-owner-v1-freeze-receipt.json
1786395090.9503542840 149850 .omo/evidence/dualcoach-candidate-manifest.json
1786383990.9423306690 13349 .omo/evidence/verify-dualcoach-candidate.py
1786379689.0112586520 3946 .omo/evidence/dualcoach-task-22-post-approval-evidence.json
1786373614.0883642200 6070 .omo/evidence/dualcoach-owner-v1-migration-runbook.md
1786373571.2141854780 2439 .omo/evidence/dualcoach-owner-v1-freeze-receipt-pre-approval.json
1786363719.6698409880 735 .omo/evidence/dualcoach-gateway-stop-receipt.md
1786361569.9869827730 320 .omo/evidence/dualcoach-owner-v1-migration-preflight.json
1786359848.6529098190 7891 .omo/evidence/dualcoach-task-19-evidence.md
1786359843.8918801510 4663 .omo/evidence/dualcoach-task-18-evidence.md
1786321508.4750174700 54769 .omo/evidence/dualcoach-task-22-focused-junit.xml
1786317782.0519495770 57787 .omo/evidence/dualcoach-task-21.md
1786268272.0284957210 11996 .omo/evidence/dualcoach-task-20-gates.json
1786268233.1916117570 2788 .omo/evidence/dualcoach-task-20-evidence.md
1786268071.0853505470 15210 .omo/evidence/dualcoach-task-20-regression-repair-junit.xml
1786267586.7994718570 3892 .omo/evidence/dualcoach-task-20-matrix-junit.xml
1786267586.5364203450 378 .omo/evidence/dualcoach-task-20-golden-junit.xml
1786267585.6584301210 770 .omo/evidence/dualcoach-task-20-review-telegram-mock-junit.xml
1786267117.0957593180 3679 .omo/evidence/dualcoach-task-20-review-failing-first-junit.xml
1786262988.0842318760 3892 .omo/evidence/dualcoach-task-19-junit.xml
1786262979.5233774840 13470 .omo/evidence/dualcoach-task-19-matrix.json
1786261654.1423860680 378 .omo/evidence/dualcoach-task-18-junit.xml
1786258943.9478426930 5983 .omo/evidence/dualcoach-task-17.md
1786248701.2451382460 7680 .omo/evidence/dualcoach-task-16.md
1786247655.5926993570 8656 .omo/evidence/dualcoach-task-15.md
1786233335.3976690260 6318 .omo/evidence/dualcoach-task-14.md
1786227819.3720355970 6185 .omo/evidence/dualcoach-task-13.md
1786222362.0906712330 5494 .omo/evidence/dualcoach-task-12.md
1786218194.1811556190 5910 .omo/evidence/dualcoach-task-11.md
1786209969.8851742960 5185 .omo/evidence/dualcoach-task-10.md
1786207528.6629904700 4807 .omo/evidence/dualcoach-task-9.md
1786205066.0765914220 4712 .omo/evidence/dualcoach-task-8.md
1786202840.9587092310 4787 .omo/evidence/dualcoach-task-7.md
1786176094.6905748360 2159 .omo/evidence/dualcoach-task-5.md
1786034034.1012281450 2926 .omo/evidence/dualcoach-task-4.md
1786031687.8484503330 692 .omo/evidence/dualcoach-task-3-verification.md
1786031285.7635937210 10238 .omo/evidence/dualcoach-recovery-runbook.md
1786030425.0126068120 677 .omo/evidence/dualcoach-task-2-verification.md
1786030174.4848856090 10935 .omo/evidence/dualcoach-golden-path-contract.md
1786029140.9653263160 1447 .omo/evidence/dualcoach-task-1-verification.md

Task22-current candidate references:.omo/senpi-task/children/st_019ff55e/evidence/st_019ff55e-readonly-terminal-audit.redacted.json:1:{"audited_child":{"actions":{"recovery_invocation":0,"replay_invocation":0,"target_start":0,"target_stop":0,"telegram_contact":0,"tool_calls":9,"verifier":0},"error":"provider overloaded after 74422 ms","executed_tool_classes":["read-only bash inspection only"],"task_id":"st_019ff55c","terminal_status":"error","transcript_bytes":216704,"transcript_sha256":"a3afac3f6fe5bf17e042a40b46cdcd966e4484c59526af87595c2c34eadf0ec6"},"callback_and_terminal":{"callback_count":1,"callback_event_integrity":"987cdde4e245b1d316ab5000271a755db148153b5207156b8082f97c2a1f5c18","callback_hmac_valid":true,"callbacks_file":{"inode":33165840,"mtime_ns":1786524363931691426,"sha256":"b2b4e23c98c2f982df4e18fa254f8520b4d5f47d2613831b36f927a398c47ced","size":964},"ingress":{"inode":30577063,"mtime_ns":1786524363940621540,"sha256":"4397d33c801618e9ae37c5b56ca5e4c362464dcfd546e5611600e763f793c542","size":401},"ingress_terminal_failure_update_629525051":true,"review_commit_digest_valid":true,"review_message_id":122,"review_update_id":629525051,"session_projection":{"inode":33165818,"mtime_ns":1786527367064827957,"sha256":"d1b4365c9a097f553da4ec08f6a1b560f718b05e15b3775f5d1011441ba24b42","size":875},"session_projection_matches_gen28":true,"terminal_binding_valid":true,"terminal_receipt_matches_gen28":true,"terminal_transition_digest_valid":true},"canonical_code_only_pins":{"candidate":"3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc","freeze":"5c98ffeb84961eb91ae33e184094f2382ceff14ad6110263ffdb6ca860f23b48","manifest":"e456cfc6c93d97989319818879a38237cd70c0d516ae5d2e33543a59962658e8","result":"pass","wheel":"f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2","wheel_receipt":"188a2e0451ddc52ef583d232d53f6ae51ba6db7ae4d078e50c9fe0f9df4a8663"},"captured_at_utc":"2026-08-12T09:55:48Z","classification":"E","classification_reason":"Authenticated terminal PUBLICATION_COMMITTED state, exactly one gen28 receipt, matching session projection, and active/running target service.","continuation_point":"Terminal is already committed. Do not invoke --recover-exact or any replay: generation 28/message 123 with its authenticated receipt already exists. Only a future canonical activation flow may consult the terminal activation gate; it must not republish gen28.","holders":{"gateway_lock_holder_pid":675692,"lock_lines":[" 675692 hermes          POSIX READ  /home/cube/.hermes/profiles/dualcoachtest/state.db"," 675692 hermes          POSIX READ  /home/cube/.hermes/profiles/dualcoachtest/state.db-shm"," 675692 hermes          FLOCK WRITE /home/cube/.hermes/profiles/dualcoachtest/gateway.lock"],"supplemental_and_outbox_lock_holders_observed":false},"invariants":{"answer_count":22,"answers_digest":"f1aced0350c473a01aa4e1382bc810e072fccfe84ccd3ea8780ee83c324dfe55","answers_digest_matches_pinned":true,"customer_enabled_false":true,"customer_service_states_empty":true,"customer_singleton":true,"downstream_authority_exists":{"activation_audit":false,"activation_journal":false,"activation_notices":false,"draft_deliveries":false,"scheduled_deliveries":false,"target_feature_epoch":false},"readiness_activation_enabled_false":true,"readiness_delivery_enabled_false":true,"ready_owner_reviewed":true,"ready_state":"ready","trainer_absent":true},"publications":{"emergency":{"inode":33166511,"mtime_ns":1786339548168113333,"sha256":"8df495c29a91042a06dc8824092760b999408328d972639595bbbc9083a50e48","size":78},"emergency_record_count":0,"gen27":{"count":1,"dispatch_identity":"c6e06b11125e4ea6e17c30d8b372a382ba6cff4dadb19d9ff0de3bfab844c239","message_id":122,"payload_digest":"189e158a6e8a5380cc7899d5d7a6ad5bbe5ae95fe38cb3c4086f119cdea2772d","receipt_hmac_valid":true,"receipt_integrity":"85d05e0ee400850435a6a8b670c840772b62779b1ec3c1db64fcfbcca9514cb4","send_count":1},"gen28":{"count":1,"dispatch_identity":"195fa9fe441e093a9a11438cf45aaee0105a6f934da5bb138a532c5f7f83efa9","message_id":123,"payload_digest":"c77c52d6051c16e340427fd6650bebfd616f28c64bcc739b34a006ef457e00c6","receipt_hmac_valid":true,"receipt_integrity":"07b8c5dd700b218bf8a315bc57192e54b2de692b0a5f4d3517afdd4e7b58b31e","send_count":1},"ledger":{"inode":33165816,"mtime_ns":1786527367066813137,"sha256":"9d8ccbb1b5268733c107f1eb631dda7e45c15e6302fb9c41a66e710b2f2db2d2","size":18146}},"redaction":"No secret material, Telegram credential, route, actor identifier, callback data, or answer content is stored.","schema":"task22-readonly-overload-audit-v1","supplemental_journal":{"active_code_candidate_is_compatibility_successor":true,"integrity_hmac_valid":true,"journal_historical_candidate_digest":"c160085a64454c23f789464170b299543324c743a489bac68ed5430fab42d291","metadata":{"inode":33165817,"mtime_ns":1786527367078034729,"sha256":"f5fc5eebff8970c72270e05b56f88baa83d69404ef4098bacae6ba2ef8af612e","size":24504},"phase":"PUBLICATION_COMMITTED","phase_events":[{"phase":"PREPARED","sequence":0},{"phase":"REVIEW_COMMITTED","sequence":1},{"phase":"PUBLICATION_COMMITTED","sequence":2}],"review_commit_digest":"1767d93699066594381b569f93d4434f5ce7304e9852a49160267916e27d9fd9","terminal_transition_digest":"25a62c64ec99a17360cad43840294152efa8c022298d5848d22e61e15af18364","transaction_id":"2d42082ae69c1aafbaa35827ea74c512f012e9fe0f92a9abe3983a816e2fcb70"},"target_unit":{"properties":{"ActiveState":"active","ExecMainStartTimestamp":"Wed 2026-08-12 18:36:04 KST","Id":"hermes-gateway-dualcoachtest.service","InvocationID":"e826bc3290fc4c74a5093bda86f7394c","MainPID":"675692","Result":"success","SubState":"running"},"ps_pid_matches_main":true},"this_audit_actions":{"canonical_code_only_verifier":1,"recovery_invocation":0,"replay_invocation":0,"target_start":0,"target_stop":0,"telegram_contact":0}}
--
.omo/evidence/verify-task22-supplemental-candidate.py-220-        "source_epochs": [1700000000, 1800000000],
.omo/evidence/verify-task22-supplemental-candidate.py-221-        "verification": {
.omo/evidence/verify-task22-supplemental-candidate.py:222:            "mode": "offline-code-only-compatibility-freeze",
.omo/evidence/verify-task22-supplemental-candidate.py-223-            "live_profile_accessed": False,
.omo/evidence/verify-task22-supplemental-candidate.py-224-        },
--
.omo/evidence/verify-task22-supplemental-candidate.py-275-        raise RuntimeError("successor predecessor binding is invalid")
.omo/evidence/verify-task22-supplemental-candidate.py-276-    if successor.get("verification") != {
.omo/evidence/verify-task22-supplemental-candidate.py:277:        "mode": "offline-code-only-compatibility-reseal",
.omo/evidence/verify-task22-supplemental-candidate.py-278-        "predecessor_candidate_retained": True,
.omo/evidence/verify-task22-supplemental-candidate.py-279-        "live_profile_accessed": False,
--
.omo/evidence/verify-task22-supplemental-candidate.py-391-        "changed_paths": changed,
.omo/evidence/verify-task22-supplemental-candidate.py-392-        "manifest": hashlib.sha256(successor_bytes).hexdigest(),
.omo/evidence/verify-task22-supplemental-candidate.py:393:        "mode": "narrow-code-only-compatibility-verification",
.omo/evidence/verify-task22-supplemental-candidate.py-394-        "paths": len(leaves),
.omo/evidence/verify-task22-supplemental-candidate.py-395-        "wheel": wheel["wheel_sha256"],
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json-1-{
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json-2-  "schema": "task22-supplemental-deterministic-wheel-v1",
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json:3:  "candidate_digest": "3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc",
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json:4:  "candidate_manifest_sha256": "e456cfc6c93d97989319818879a38237cd70c0d516ae5d2e33543a59962658e8",
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json-5-  "predecessor_manifest_sha256": "bd0c6dbbf26e6f6d50e9d03db0ba8e7725da8d4a3cdd377f8eefacb9900732ea",
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json-6-  "wheel_filename": "dualcoach-task22-supplemental-compatibility.whl",
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json:7:  "wheel_sha256": "f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2",
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json-8-  "wheel_bytes": 8476988,
.omo/evidence/dualcoach-task22-supplemental-compatibility-wheel.json-9-  "wheel_members": 989,
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-90-  },
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-91-  "candidate_diff_sha256": "25fc2fc9a8a5d2c6a662ab289bd070d93c4e4363aa6deaa22a0eb695c679c88e",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json:92:  "candidate_digest": "3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-93-  "candidate_files": [
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-94-    {
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6116-  },
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6117-  "predecessor_verification": {
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json:6118:    "mode": "offline-code-only-successor-reseal",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6119-    "predecessor_candidate_retained": true,
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6120-    "live_profile_accessed": false
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6150-    "receipt_schema": "task22-supplemental-deterministic-wheel-v1",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6151-    "filename": "dualcoach-task22-supplemental-compatibility.whl",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json:6152:    "sha256": "f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6153-    "bytes": 8476988,
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6154-    "members": 989,
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6165-  },
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6166-  "verification": {
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json:6167:    "mode": "offline-code-only-compatibility-reseal",
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6168-    "predecessor_candidate_retained": true,
.omo/evidence/dualcoach-task22-supplemental-compatibility-manifest.json-6169-    "live_profile_accessed": false
--
.omo/evidence/reseal-task22-supplemental-candidate.py-2-"""Create the Task22 supplemental compatibility implementation successor.
.omo/evidence/reseal-task22-supplemental-candidate.py-3-
.omo/evidence/reseal-task22-supplemental-candidate.py:4:This code-only operation never opens the live onboarding state. It chains from
.omo/evidence/reseal-task22-supplemental-candidate.py-5-the pinned supplemental authority successor, preserving its declared candidate
.omo/evidence/reseal-task22-supplemental-candidate.py-6-and repository-status scopes while refreshing only the declared source leaves.
--
.omo/evidence/reseal-task22-supplemental-candidate.py-311-        },
.omo/evidence/reseal-task22-supplemental-candidate.py-312-        "verification": {
.omo/evidence/reseal-task22-supplemental-candidate.py:313:            "mode": "offline-code-only-compatibility-reseal",
.omo/evidence/reseal-task22-supplemental-candidate.py-314-            "predecessor_candidate_retained": True,
.omo/evidence/reseal-task22-supplemental-candidate.py-315-            "live_profile_accessed": False,
--
.omo/evidence/reseal-task22-supplemental-candidate.py-321-        "changed_paths": changed,
.omo/evidence/reseal-task22-supplemental-candidate.py-322-        "manifest": hashlib.sha256(SUCCESSOR.read_bytes()).hexdigest(),
.omo/evidence/reseal-task22-supplemental-candidate.py:323:        "mode": "code-only-compatibility-reseal",
.omo/evidence/reseal-task22-supplemental-candidate.py-324-        "paths": len(leaves),
.omo/evidence/reseal-task22-supplemental-candidate.py-325-    }, sort_keys=True))
--
.omo/evidence/dualcoach-task22-supplemental-successor-manifest.json-6199-  },
.omo/evidence/dualcoach-task22-supplemental-successor-manifest.json-6200-  "verification": {
.omo/evidence/dualcoach-task22-supplemental-successor-manifest.json:6201:    "mode": "offline-code-only-successor-reseal",
.omo/evidence/dualcoach-task22-supplemental-successor-manifest.json-6202-    "predecessor_candidate_retained": true,
.omo/evidence/dualcoach-task22-supplemental-successor-manifest.json-6203-    "live_profile_accessed": false
--
.omo/evidence/dualcoach-task-22-evidence-index.json-31-  },
.omo/evidence/dualcoach-task-22-evidence-index.json-32-  "active_pins": {
.omo/evidence/dualcoach-task-22-evidence-index.json:33:    "candidate": "3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc",
.omo/evidence/dualcoach-task-22-evidence-index.json:34:    "manifest": "e456cfc6c93d97989319818879a38237cd70c0d516ae5d2e33543a59962658e8",
.omo/evidence/dualcoach-task-22-evidence-index.json:35:    "freeze": "5c98ffeb84961eb91ae33e184094f2382ceff14ad6110263ffdb6ca860f23b48",
.omo/evidence/dualcoach-task-22-evidence-index.json:36:    "wheel": "f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2",
.omo/evidence/dualcoach-task-22-evidence-index.json-37-    "code_only_verifier": "PASS"
.omo/evidence/dualcoach-task-22-evidence-index.json-38-  },
--
.omo/evidence/dualcoach-task-22.md-74-historical trainer branch into v1 evidence.
.omo/evidence/dualcoach-task-22.md-75-
.omo/evidence/dualcoach-task-22.md:76:## Active code-only pins
.omo/evidence/dualcoach-task-22.md-77-
.omo/evidence/dualcoach-task-22.md:78:- Candidate: `3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc`.
.omo/evidence/dualcoach-task-22.md:79:- Manifest: `e456cfc6c93d97989319818879a38237cd70c0d516ae5d2e33543a59962658e8`.
.omo/evidence/dualcoach-task-22.md:80:- Freeze: `5c98ffeb84961eb91ae33e184094f2382ceff14ad6110263ffdb6ca860f23b48`.
.omo/evidence/dualcoach-task-22.md:81:- Wheel: `f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2`.
.omo/evidence/dualcoach-task-22.md:82:- Canonical code-only verifier: `PASS`.
.omo/evidence/dualcoach-task-22.md-83-
.omo/evidence/dualcoach-task-22.md-84-## Non-touch and residuals
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-1-{
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-2-  "schema": "task22-supplemental-compatibility-freeze-v1",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json:3:  "candidate_digest": "3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json:4:  "manifest_sha256": "e456cfc6c93d97989319818879a38237cd70c0d516ae5d2e33543a59962658e8",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-5-  "predecessor_candidate_digest": "cae3f885c66e6c1174f8761ec47db4ba585eba034a6f275f3e81fc5e986401ba",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-6-  "predecessor_manifest_sha256": "bd0c6dbbf26e6f6d50e9d03db0ba8e7725da8d4a3cdd377f8eefacb9900732ea",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json:7:  "wheel_sha256": "f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-8-  "wheel_members": 989,
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-9-  "wheel_receipt_sha256": "38480bd0bc2e1e3276b43264cd944e8e501da1efc5b0573b264de33becc0d9bf",
--
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-14-  ],
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-15-  "verification": {
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json:16:    "mode": "offline-code-only-compatibility-freeze",
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-17-    "live_profile_accessed": false
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-18-  },
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json:19:  "receipt_digest": "5c98ffeb84961eb91ae33e184094f2382ceff14ad6110263ffdb6ca860f23b48"
.omo/evidence/dualcoach-task22-supplemental-compatibility-freeze-receipt.json-20-}
--
.omo/evidence/task22-supplemental-compatibility-canonical.py-1-#!/usr/bin/env -S python3 -I -B
.omo/evidence/task22-supplemental-compatibility-canonical.py:2:"""Canonical, code-only verifier for the Task22 compatibility implementation."""
.omo/evidence/task22-supplemental-compatibility-canonical.py-3-
.omo/evidence/task22-supplemental-compatibility-canonical.py-4-from __future__ import annotations
--
.omo/evidence/task22-supplemental-compatibility-canonical.py-16-EXPECTED_VERIFIER_SHA256: Final = "fd57070f322f4de6a8a416d2c306652deb4313d8ba2aa44dba0aaf9e43f08bd2"
.omo/evidence/task22-supplemental-compatibility-canonical.py-17-EXPECTED: Final = {
.omo/evidence/task22-supplemental-compatibility-canonical.py:18:    "candidate": "3ce5b95637211b0289529c35b452cf1771fe868a34777140388a8caa75e183cc",
.omo/evidence/task22-supplemental-compatibility-canonical.py-19-    "changed_paths": [
.omo/evidence/task22-supplemental-compatibility-canonical.py-20-        "gateway:gateway/platforms/telegram_nutrition_onboarding_supplemental_review.py",
--
.omo/evidence/task22-supplemental-compatibility-canonical.py-22-        "gateway:tests/gateway/test_task22_supplemental_owner_review.py",
.omo/evidence/task22-supplemental-compatibility-canonical.py-23-    ],
.omo/evidence/task22-supplemental-compatibility-canonical.py:24:    "freeze": "5c98ffeb84961eb91ae33e184094f2382ceff14ad6110263ffdb6ca860f23b48",
.omo/evidence/task22-supplemental-compatibility-canonical.py:25:    "manifest": "e456cfc6c93d97989319818879a38237cd70c0d516ae5d2e33543a59962658e8",
.omo/evidence/task22-supplemental-compatibility-canonical.py:26:    "mode": "narrow-code-only-compatibility-verification",
.omo/evidence/task22-supplemental-compatibility-canonical.py-27-    "paths": 177,
.omo/evidence/task22-supplemental-compatibility-canonical.py:28:    "wheel": "f592abefa3deca172c9d0b8dcb8013e0e8a1fc97ff03db4054adb3f0b6f3abf2",
.omo/evidence/task22-supplemental-compatibility-canonical.py-29-    "wheel_receipt": "188a2e0451ddc52ef583d232d53f6ae51ba6db7ae4d078e50c9fe0f9df4a8663",
.omo/evidence/task22-supplemental-compatibility-canonical.py-30-}
--
.omo/evidence/task22-supplemental-compatibility-canonical.py-60-        raise RuntimeError("canonical verifier result does not match exact pins")
.omo/evidence/task22-supplemental-compatibility-canonical.py-61-    print(json.dumps({
.omo/evidence/task22-supplemental-compatibility-canonical.py:62:        "mode": "canonical-code-only-verification",
.omo/evidence/task22-supplemental-compatibility-canonical.py-63-        "ready": True,
.omo/evidence/task22-supplemental-compatibility-canonical.py-64-        "successor": EXPECTED,
--
.omo/evidence/task22-supplemental-compatibility-canonical.py-71-        raise SystemExit(main())
.omo/evidence/task22-supplemental-compatibility-canonical.py-72-    except (OSError, RuntimeError, ValueError, json.JSONDecodeError) as exc:
.omo/evidence/task22-supplemental-compatibility-canonical.py:73:        print(json.dumps({"mode": "canonical-code-only-verification", "ready": False, "error": str(exc)}, sort_keys=True), file=sys.stderr)
.omo/evidence/task22-supplemental-compatibility-canonical.py-74-        raise SystemExit(1)


--- customers/registry.json ---
sha256 69c1382eb663942a7c8c18b9fee29141612ac37e92f1ffb7df0ba1beed44579d bytes 5791 root_type dict
matching_records 1
{'customer_key': 'task22_dm_rehearsal', 'enabled': False}
--- data/owner-actions/customer-service-state.json ---
sha256 81c1e581cb84aac6f44044747e0e8e77a2d7498c71d626f1f9b5ec2f9d09a261 bytes 135 root_type dict
keys ['payload_digest', 'schema', 'states']
task22_mentions 0
--- data/owner-actions/draft-deliveries.json ---
READ_ERROR FileNotFoundError [Errno 2] No such file or directory: '/home/cube/.hermes/profiles/dualcoachtest/data/owner-actions/draft-deliveries.json'
--- gateway_state.json ---
sha256 8725fab54f2ea934db9b8a25467287df9e65f6076937cd1653724e807074f86f bytes 519 root_type dict
{'pid': 675692, 'updated_at': '2026-08-12T09:36:08.879971+00:00'}


**Assessing status command side effects**

**Locating task22 session receipts**

**Planning task22 verification script run**

**Locating task22 session receipts**

**Inspecting preflight mutation handling**

**Analyzing provider membership receipt handling**

**Verifying gateway service status methods**

**Inspecting Telegram activation command integration**

**Evaluating synthetic activation feasibility**

**Planning source code inspection**

**Planning test execution and validation**

**Implementing safe JSON inspection**

**Planning source code inspection**

**Implementing safe JSON inspection**

bash

python3 -I -B .omo/evidence/task22-supplemental-compatibility-canonical.py

cd /home/cube/projects/richard/hermes-agent && .venv/bin/python -m hermes_cli.main --profile dualcoachtest gateway status 2>&1; printf '\n-- service subcommand help --\n'; .venv/bin/python -m hermes_cli.main --profile dualcoachtest gateway restart --help 2>&1; printf '\n-- process service unit --\n'; systemctl --user show hermes-gateway-dualcoachtest.service --no-page --property=Id,LoadState,ActiveState,SubState,MainPID,ExecMainStartTimestamp,Result,FragmentPath 2>&1

python3 - <<'PY'
from __future__ import annotations
import hashlib,json
from pathlib import Path
root=Path('/home/cube/.hermes/profiles/dualcoachtest')
needle='task22_dm_rehearsal'
for path in sorted((root/'data').rglob('*.json')):
    try:
        raw=path.read_bytes(); value=json.loads(raw)
    except Exception:
        continue
    if needle not in raw.decode('utf-8','replace'):
        continue
    rel=path.relative_to(root)
    summary={'path':str(rel),'sha256':hashlib.sha256(raw).hexdigest(),'bytes':len(raw)}
    if isinstance(value,dict):
        for key in ('schema','state','phase','customer_key','session_id','generation','enabled','activation_enabled','delivery_enabled','starts_on','daily_time','customer_user_id','current_customer'):
            if key in value and key not in {'customer_user_id'}:
                summary[key]=value[key] if not isinstance(value[key],(dict,list)) else type(value[key]).__name__
        if 'sessions' in value and isinstance(value['sessions'],list):
            summary['sessions']=[{k:item.get(k) for k in ('session_id','customer_key','state','generation') if k in item} for item in value['sessions'] if isinstance(item,dict) and item.get('customer_key')==needle]
    print(json.dumps(summary,sort_keys=True))
print('provider_receipt_candidates')
for path in sorted((root/'data').rglob('*')):
    if path.is_file() and any(token in path.name.lower() for token in ('membership','provider','readiness','checklist','activation')):
        print(path.relative_to(root))
PY

cd /home/cube/projects/richard/hermes-agent && printf '%s\n' 'Actual inbound start/check-in paths:'; rg -n -C 3 'start_nutrition|nutrition_daily|open_launcher\("nutrition_daily"|/checkin|체크인 시작|체크인.*시작|activation.*notice|drain_activation' gateway/platforms/telegram.py gateway/platforms/nutrition_coaching.py gateway/platforms/physique_checkin.py tests/gateway/test_telegram_physique_checkin.py | head -900; printf '\nDedicated Task23/test-surface candidates:'; rg -n -i -C 2 'synthetic.*activation|activation.*rehearsal|test.*surface|Telegram.*client|user.*update|human.*client' gateway/platforms tests/gateway scripts | head -600

printf '%s\n' 'Profile runtime state filenames:'; find /home/cube/.hermes/profiles/dualcoachtest/data -maxdepth 5 -type f -printf '%p\n' | sort | grep -E 'task22|checkin|nutrition|activation|schedule|owner-action|bootstrap' | head -600; printf '\nTask22 current receipt directory:'; find /home/cube/.hermes/profiles/dualcoachtest/data/customers/task22_dm_rehearsal -maxdepth 5 -type f -printf '%p\n' | sort | head -600

printf '%s\n' 'Supported checkin-cli executable entry points:'; find /home/cube/.hermes/profiles/dualcoachtest/workspace/checkin_cli -maxdepth 2 -type f \( -name '*.py' -o -name 'pyproject.toml' \) -printf '%p\n' | sort | grep -E '(__main__|cli|main|pyproject)' ; printf '\nCLI/parser declarations:'; rg -n -C 2 'Typer\(|ArgumentParser\(|def main\(|console_scripts|project\.scripts' /home/cube/.hermes/profiles/dualcoachtest/workspace/checkin_cli/checkin_cli /home/cube/.hermes/profiles/dualcoachtest/workspace/checkin_cli/pyproject.toml | head -600


{"error": "canonical verifier requires the isolated Task22 virtual environment", "mode": "canonical-code-only-verification", "ready": false}

Command exited with code 1


● hermes-gateway-dualcoachtest.service - Hermes Agent Gateway - Messaging Platform Integration
     Loaded: loaded (/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service; enabled; preset: enabled)
     Active: active (running) since Wed 2026-08-12 18:36:04 KST; 46min ago
 Invocation: e826bc3290fc4c74a5093bda86f7394c
   Main PID: 675692 (hermes)
      Tasks: 8 (limit: 72808)
     Memory: 163.7M (peak: 176.3M)
        CPU: 3.947s
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/hermes-gateway-dualcoachtest.service
             └─675692 /home/cube/projects/richard/hermes-agent/.venv/bin/python -m hermes_cli.main --profile dualcoachtest gateway run

 8월 12 18:36:04 cube-B850M-AORUS-ELITE-WIFI6E-ICE systemd[2585]: Started hermes-gateway-dualcoachtest.service - Hermes Agent Gateway - Messaging Platform Integration.
 8월 12 18:36:07 cube-B850M-AORUS-ELITE-WIFI6E-ICE python[675692]: WARNING gateway.platforms.telegram: [Telegram] onboarding card recovery failed for bootstrap session
✓ User gateway service is running
✓ Systemd linger is enabled (service survives logout)

-- service subcommand help --
usage: hermes gateway restart [-h] [--system] [--all]

options:
  -h, --help  show this help message and exit
  --system    Target the Linux system-level gateway service
  --all       Kill ALL gateway processes across all profiles before restarting

-- process service unit --
MainPID=675692
Result=success
ExecMainStartTimestamp=Wed 2026-08-12 18:36:04 KST
Id=hermes-gateway-dualcoachtest.service
LoadState=loaded
ActiveState=active
SubState=running
FragmentPath=/home/cube/.config/systemd/user/hermes-gateway-dualcoachtest.service