The lab's phone shows an HTML message as Telegram does, so a tap carries back the words shown (issue 692) #104

Merged
mesh-admin merged 1 commits from fix/692-the-fake-phone-reads-html into main 2026-10-14 00:45:34 +00:00
Contributor

Cause. Since mesh-catalog #278 (issues 383/639), the Telegram channel sends every message with parse_mode: HTML: the title in <b>, key lines in <code>, the note in <blockquote expandable>, and <, >, & escaped. The real Bot API reads that markup and gives back plain text on a tap (callback_query.message.text), which is what the channel's tap check digests (wordsDigest). The lab's fake Bot API (asks/faketelegram_test.go) kept the text exactly as sent, tags and entities included, and returned that on a tap. The digests differed, so the proof's tap was refused with "That answer was not taken: this message does not show what the mesh put there".

Live impact: none. operator-channel.asks shows every ask since #278 was delivered (2026-10-13 21:16 local) answered on Telegram, for example ADR 0353 at 23:42, 0349, 0350, 0352, 0348 and 0351, all chosen via telegram with user id verified. The telegram-runtime journal on novox has no refused tap since then.

Fix. For sendMessage/editMessageText with parse_mode: HTML, the fake now stores and returns the message as Telegram shows it: tags removed, entities unescaped (shownOfHTML). The channel's code is unchanged.

Tests. TestTheFakePhoneShowsHTMLAsTelegramDoes (new; asks/merge-check.sh now runs it too). The end-to-end proof TestTheOperatorsAnswerEndToEnd, which fails on main against mesh-catalog main (mesh-lab#103's repo-check), is the proof of the fix: this PR's repo-check runs it against the catalogue the mesh runs. go vet and gofmt are clean. I ran the unit test locally; the four-minute end-to-end proof runs on the build seat.

Fixes issue 692.

**Cause.** Since mesh-catalog #278 (issues 383/639), the Telegram channel sends every message with `parse_mode: HTML`: the title in `<b>`, key lines in `<code>`, the note in `<blockquote expandable>`, and `<`, `>`, `&` escaped. The real Bot API reads that markup and gives back plain text on a tap (`callback_query.message.text`), which is what the channel's tap check digests (`wordsDigest`). The lab's fake Bot API (`asks/faketelegram_test.go`) kept the text exactly as sent, tags and entities included, and returned that on a tap. The digests differed, so the proof's tap was refused with "That answer was not taken: this message does not show what the mesh put there". **Live impact: none.** `operator-channel.asks` shows every ask since #278 was delivered (2026-10-13 21:16 local) answered on Telegram, for example ADR 0353 at 23:42, 0349, 0350, 0352, 0348 and 0351, all `chosen` via telegram with user id verified. The `telegram-runtime` journal on novox has no refused tap since then. **Fix.** For `sendMessage`/`editMessageText` with `parse_mode: HTML`, the fake now stores and returns the message as Telegram shows it: tags removed, entities unescaped (`shownOfHTML`). The channel's code is unchanged. **Tests.** `TestTheFakePhoneShowsHTMLAsTelegramDoes` (new; `asks/merge-check.sh` now runs it too). The end-to-end proof `TestTheOperatorsAnswerEndToEnd`, which fails on main against mesh-catalog main (mesh-lab#103's repo-check), is the proof of the fix: this PR's repo-check runs it against the catalogue the mesh runs. go vet and gofmt are clean. I ran the unit test locally; the four-minute end-to-end proof runs on the build seat. Fixes issue 692.
mesh-admin added 1 commit 2026-10-14 00:37:18 +00:00
The lab's phone shows an HTML message as Telegram does, so a tap carries back the words shown (issue 692)
mesh/merge-gate head dcaf843b8a6e merged into trunk 1316a405a630 — pass: the change touches no module of the mesh's graph
mesh/repo-check head dcaf843b8a6e merged into trunk 1316a405a630 — pass: THE CHANGE ALTERS ITS OWN CHECK (asks/merge-check.sh): main's version judged it; t…
mesh/delivery delivered
dcaf843b8a
Since mesh-catalog #278 the channel sends every message in HTML (issues 383, 639). The fake Bot API kept
the text as sent, tags and entities included, and gave that back on a tap; the channel compares a tap by
the plain words it showed, so the proof's tap was refused as "this message does not show what the mesh put
there". Telegram reads the markup and gives the plain words back; the fake now does too.
Author
Contributor

Delivery novox/mesh-lab@dcaf843b8a6e — delivered since 2026-10-14T00:45:43Z

Delivery plan — builds nothing: the change touches no module of the mesh's graph

Transitions

  • 2026-10-14 00:37 (new) → proposed (announced): novox/mesh-lab#104's head announced
  • 2026-10-14 00:41 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
  • 2026-10-14 00:41 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-14 00:45 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-14 00:45 published → delivered (done): nothing for a walk to move

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-lab@dcaf843b8a6e` — **delivered** since 2026-10-14T00:45:43Z **Delivery plan** — builds nothing: the change touches no module of the mesh's graph **Transitions** - 2026-10-14 00:37 (new) → proposed (announced): novox/mesh-lab#104's head announced - 2026-10-14 00:41 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit - 2026-10-14 00:41 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-14 00:45 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-14 00:45 published → delivered (done): nothing for a walk to move The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
mesh-admin left a comment
Author
Contributor

Verdict: APPROVE at dcaf843b — correct, and the proof still proves what it says; merge before #103.

Independent review (spawned by the main session on g14; not the author).

Checks at this head:

  • mesh/merge-gate: success — "PASS — the change touches no module of the mesh's graph" (build-1791938266390971910, shanks)
  • mesh/repo-check: success — "PASS — THE CHANGE ALTERS ITS OWN CHECK (asks/merge-check.sh): main's version judged it; ... its merge-check.sh passed, each part in its toolchain". TestTheOperatorsAnswerEndToEnd passed all 9 subtests against mesh-catalog ebf7f377, the catalogue the mesh runs.
  • mesh/delivery: success — "ready: it delivers once merged"

What I checked:

  1. The conversion against the channel (mesh-catalog modules/telegram/cmd/telegram/render.go, channel.go). The channel sends <b> title, <code> key lines, <blockquote expandable> note, and escapes only & < > (escape()). shownOfHTML strips tags first and unescapes after, so an escaped &lt;b&gt; in the words comes back as the literal <b> and is not stripped, and &amp;lt; comes back as &lt; (one pass). That is what Telegram gives back in message.text for these tags. The <blockquote expandable> attribute is covered by [^>]*. html.UnescapeString covers " and numeric entities too.
  2. The digest still has teeth. Step 3 (approve) now matches, so step 5's tap via tappedOn with "Flip ALL the switches?" is refused by the channel's wordsDigest comparison against a digest that is otherwise known to match; before this change every tap was refused, so the tampered-message refusal proved nothing. Steps 2, 7 and 8 still require the router's refusal, which is reached only after the channel's word check passes.
  3. Locally (niced): gofmt clean, go vet clean, TestTheFakePhoneShowsHTMLAsTelegramDoes passes. Main's merge-check.sh judged this PR, so the new unit test has not yet run on the build seat; it runs from the next PR on.

Non-blocking notes:

  • The fake is more lenient than Telegram: html.UnescapeString accepts every named entity (Telegram supports only < > & " and numeric ones and rejects others), and a bare < not followed by a letter is left as is where Telegram would refuse the message. The channel never sends either, so the proof is unaffected; a stricter fake could refuse them to catch a future escaping regression in the channel.
  • The unit test does not exercise ". Adding it to the input would match the entity list the issue names.
  • Step 5 could also assert the tamper toast ("does not show what the mesh put there") via toldTapper, so the tampered tap is proven refused for that reason and not dropped for another.
Verdict: APPROVE at dcaf843b — correct, and the proof still proves what it says; merge before #103. Independent review (spawned by the main session on g14; not the author). Checks at this head: - mesh/merge-gate: success — "PASS — the change touches no module of the mesh's graph" (build-1791938266390971910, shanks) - mesh/repo-check: success — "PASS — THE CHANGE ALTERS ITS OWN CHECK (asks/merge-check.sh): main's version judged it; ... its merge-check.sh passed, each part in its toolchain". TestTheOperatorsAnswerEndToEnd passed all 9 subtests against mesh-catalog ebf7f377, the catalogue the mesh runs. - mesh/delivery: success — "ready: it delivers once merged" What I checked: 1. The conversion against the channel (mesh-catalog modules/telegram/cmd/telegram/render.go, channel.go). The channel sends `<b>` title, `<code>` key lines, `<blockquote expandable>` note, and escapes only & < > (escape()). shownOfHTML strips tags first and unescapes after, so an escaped `&lt;b&gt;` in the words comes back as the literal `<b>` and is not stripped, and `&amp;lt;` comes back as `&lt;` (one pass). That is what Telegram gives back in message.text for these tags. The `<blockquote expandable>` attribute is covered by `[^>]*`. html.UnescapeString covers &quot; and numeric entities too. 2. The digest still has teeth. Step 3 (approve) now matches, so step 5's tap via tappedOn with "Flip ALL the switches?" is refused by the channel's wordsDigest comparison against a digest that is otherwise known to match; before this change every tap was refused, so the tampered-message refusal proved nothing. Steps 2, 7 and 8 still require the router's refusal, which is reached only after the channel's word check passes. 3. Locally (niced): gofmt clean, go vet clean, TestTheFakePhoneShowsHTMLAsTelegramDoes passes. Main's merge-check.sh judged this PR, so the new unit test has not yet run on the build seat; it runs from the next PR on. Non-blocking notes: - The fake is more lenient than Telegram: html.UnescapeString accepts every named entity (Telegram supports only &lt; &gt; &amp; &quot; and numeric ones and rejects others), and a bare `<` not followed by a letter is left as is where Telegram would refuse the message. The channel never sends either, so the proof is unaffected; a stricter fake could refuse them to catch a future escaping regression in the channel. - The unit test does not exercise &quot;. Adding it to the input would match the entity list the issue names. - Step 5 could also assert the tamper toast ("does not show what the mesh put there") via toldTapper, so the tampered tap is proven refused for that reason and not dropped for another.
mesh-admin merged commit 307dd23d6d into main 2026-10-14 00:45:34 +00:00
mesh-admin deleted branch fix/692-the-fake-phone-reads-html 2026-10-14 00:45:34 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-lab#104