Skip to content
C.W.K.
Stream
Lesson 03 of 04 · published

Telegram Mirrors Only Its Own

~15 min · master-chief, telegram, sync, notifications, polling

Level 0Visitor
0 XP0/33 lessons0/11 achievements
0/100 XP to next level100 XP to go0% complete

Telegram Is Two Things at Once

For most of Pippa's life Telegram was simply one more client with its own conversation. Under Master Chief it became two things that pull in opposite directions. It is a doorway: Dad types into it and the turn enters the one shared room. And it is a phone notification channel: every message the bot sends makes his phone buzz, wherever he is. A doorway wants to show the room. A notification channel should only speak when it has something to say.

The doorway half is plain. Telegram defaults to the Chief. /new (or the New button) opens a separate Telegram conversation outside it, /switch to any other row leaves it, /switch to the Chief's row or /chief brings it back, /list marks the Chief's row by name among the recent conversations, and /status says which mode the chat is in. The bot does not trust a cached id for the room: on each sync pass it asks the engine to resolve the Chief and repoints every chat bound to it, so those chats follow the Chief whatever row it is.

The Phone That Rang Twice

The bot learns about new messages by polling, and its rule from before Master Chief was sensible for a conversation Telegram owned: mirror every message another client added. If you had said something in the WebUI, you wanted to see it in Telegram too. In a room every device shares, that rule turns every exchange into a double notification. Dad asks something on the laptop, Pippa answers on the laptop, and the phone in his pocket buzzes twice for a conversation he is already reading. He ruled on it within the first two days of the Chief (coop #347; the code records it as 2026-10-08): in Master Chief, Telegram mirrors only Telegram's own turns. (Coop notices and follow-up blocker alerts still reach the phone, by their own path; the mirror is not how they travel.)

Whose Turn Is It?

That ruling needs every message to know which door it came through, and Track 2 showed where that comes from: the Chief route stamps origin_surface on every user row it admits. An assistant reply carries no doorway of its own, so _turn_origin walks up the parent chain (at most eight steps) to the nearest user row that names one, stepping past steer rows, which belong to the turn they interrupted. _mirrors_to_telegram then decides, in this order:

The messageMirrored to Telegram?
Carries a turn_kind (a Coop notice, a follow-up line, a task outcome, a compaction summary)Never. Notices already ride their own channel; a summary is bookkeeping.
Has origin_surface: scheduled-task in its host contextNever. A scheduled report owns its own notification policy.
Lives in an ordinary conversation, not the ChiefYes, the old rule: every message another client added.
Lives in the ChiefOnly if its turn's origin is telegram.

A message that is not mirrored is not ignored. It is settled: written into the bot's delivery ledger and its known set exactly as if it had been sent. That matters after a restart, when the bot rebuilds what it has already handled. Without settling, every WebUI turn would look undelivered forever and be read again on every poll.

Read the Version First

Polling an endless conversation has its own trap. On the Chief's second day the poll was reading the whole transcript every five seconds, about 2.9 MB each time, and growing. Now the poll asks a cheap question first: GET /api/conversations/{id}/version, which answers an in-memory counter and a boot epoch, a random value chosen at each engine start. The pair (epoch, version) is what the bot compares, because a restarted engine starts counting from zero and could otherwise repeat an old number for new contents. Only when the pair moved does the bot read a page of the newest 100 messages. And it records the new pair only once every message on that page is settled, so a batch cut short or a failed image send is read again on the next pass. The cold review checked this gate and listed it as sound.

A Cut Stream Is a Question, Not a Resend

Phones lose connections. When the bot's stream to the engine breaks, it never just sends the message again, because a second send would append Dad's words twice and run a duplicate turn. Every Telegram turn carries an external request id built from the chat and the message, in the format tg-<chat>-<message>. If the cut came before the engine said it had started the turn, the bot asks the engine's turn status for that id, every three seconds for up to two minutes. Missing means the engine never took it, so the bot sends it once more under the same id, which the engine would refuse to run twice. Pending or completed means the turn is in the room, and the reply arrives through the sync poll, which mirrors it because its origin is Telegram. A cut after the turn started is never resent at all.

A notification channel mirrors only what was said on it. Everything else in the room is still there when Dad opens it. The mirror copies only Telegram's own turns. Coop notices and follow-up blocker alerts reach the phone by their own path, once, because they chose to ring.

Code

Bot excerpt (verbatim; the second function's docstring, which this lesson paraphrases, is trimmed): where a turn came from, and whether Telegram shows it·python
def _turn_origin(message: dict, by_id: dict[str, dict], *, depth: int = 8) -> str | None:
    """The doorway a turn came through: the nearest user message at or above
    ``message`` in the parent chain that names an ``origin_surface`` (the
    Chief route stamps one on every admitted turn). A steer row has none and
    belongs to the turn it interrupted, so the walk continues past it."""
    current: dict | None = message
    for _ in range(depth):
        if current is None:
            return None
        if current.get("role") in ("user", "human"):
            context = current.get("host_context") or {}
            if isinstance(context, dict) and context.get("origin_surface"):
                return str(context["origin_surface"])
        current = by_id.get(current.get("parent_id") or "")
    return None


def _mirrors_to_telegram(message: dict, by_id: dict[str, dict], *, chief: bool) -> bool:
    if message.get("turn_kind"):
        return False
    context = message.get("host_context") or {}
    if isinstance(context, dict) and context.get("origin_surface") == "scheduled-task":
        return False
    if not chief:
        return True
    return _turn_origin(message, by_id) == "telegram"
Bot excerpt (trimmed from the sync poll; the sending in between is elided): version first, a 100-message page only when it moved, and the version cached only once everything read is settled·python
SYNC_WINDOW = 100

            for chat_id_str, settings in chat_map.items():
                conv_id = settings.conversation_id
                if not conv_id or conv_id in _inflight_convs:
                    continue
                version = await _conversation_version(client, conv_id)
                if (version is not None and conv_id in _sync_seeded_convs
                        and _sync_versions.get(conv_id) == version):
                    continue
                resp = await client.get(f"/api/conversations/{conv_id}/page?limit={SYNC_WINDOW}")
                ...
                for m in msgs:
                    ...
                    # Not Telegram's to show (a scheduled report, or in Chief a
                    # turn Dad had in the app or the WebUI): settled, never sent.
                    if not _mirrors_to_telegram(m, by_id, chief=chief_bound):
                        _mark_delivered(conv_id, [m["id"]])
                        _recovery_update(conv_id, [m['id']], complete=True)
                        known.add(m["id"])
                        continue
                    ...
                if version is not None and all(m["id"] in known for m in msgs):
                    _sync_versions[conv_id] = version

External links

Exercise

If you have any client that mirrors a shared stream (a chat bot, a notification relay, a sync daemon), list the last ten notifications it produced and, for each, write down which device or surface originated the underlying event. Count how many you had already seen somewhere else. Then add an origin field where events are created, and a single function that decides from that field whether this client should notify, with a test for each row of its decision table.
Hint
Make the decision a pure function of the message and its parents, with no network calls, so the test needs nothing but a few dictionaries. And decide what happens to a message you choose not to show: it must still be marked handled, or your client will keep reconsidering it forever.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.