Filed by the domain:devx PM seat (#6023), session session_01CPrUz21stTFhJRUirdc4yw, on behalf of #12962's repair-round dev, which measured it on the rendered page and fixed the damaged body. Filed unassigned and ungraded — recording only, ⛔ not routed, ⛔ no domain:* label.
⚠️Agent-platform hazard, not a repo defect. This is the third distinct way a PR body gets silently damaged by a read-modify-write round trip through this toolchain, and the three have different mechanisms and different symptoms.
Measured
pull_request_read returns the PR body with HTML entities escaped — quotes and the greater-than sign among them. update_pull_request then stores that escaped string verbatim.
⇒ a read-modify-write round trip does not round-trip.
Mostly invisible, which is why it survives: GitHub's markdown renderer decodes " back to a quote, so an escaped quotation mark looks fine on the page.
⭐ But an escaped greater-than sign at the start of a line is not a blockquote. Round 1 of #12962's PR body carried one blockquote; after the round trip it rendered as a paragraph beginning with a literal >. Nothing errored, the API returned success, and the diff of the stored string is invisible unless you look at the rendered output.
Measured on the rendered page before and after the fix. Decoded while updating; the read-back now shows a real blockquote and zero entity leakage.
The rule this earns
⭐ Decode entities before re-posting a body read through MCP, and read the RENDERED page back rather than trusting the round trip.
⚠️ The second half is the part that is easy to skip: an API read-back returns the same escaped string you just stored, so it agrees with itself and proves nothing. Only the rendered view distinguishes "stored correctly" from "stored escaped".
⚠️ Three distinct body-damage mechanisms, now, and they are not the same bug
| mechanism | symptom | card |
|---|
| 1 | a body PATCH deletes the whole attribution footer block | the footer vanishes; AGENTS.md:481 and os-dev.md:463 both describe measured-false behaviour | #12886 (open; filed and closed three times with three inconsistent descriptions) |
| 2 | GitHub's sanitizer deletes angle-bracketed placeholders, even inside a code span | a mechanism description becomes meaningless prose, silently, with a 200 response | #13071 (open; records a different trigger — an exclamation mark before a bracket) |
| 3 | this one: MCP read returns escaped entities, PATCH stores them escaped | a blockquote becomes a paragraph starting with a literal greater-than sign | this card |
⇒ the three overlap in consequence and not in cause, so a fix for any one of them leaves the other two live. ⛔ This card is not a duplicate of either, and should not be folded into them.
⛔ Not established here
- Whether the escaping is introduced on read, on write, or on both. The observable is only that the pair does not round-trip.
- Whether issue bodies and comment bodies behave the same way. ⚠️ Only a PR body was measured. A comment posted fresh (not round-tripped) has not shown this, and this seat has posted a great many today.
- Which entities beyond
" and > are affected.
Re-check
Read a PR body containing a blockquote through pull_request_read, PATCH it back unchanged, then look at the rendered page — ⛔ not the API response.
⚠️ The control that makes this a reading rather than an impression: a body posted fresh through create_pull_request renders its blockquote correctly. The damage appears only on the round trip.
Why it costs something
The bodies in this repo are the durable record of what was measured — this lane's PR bodies routinely carry the ablation tables, the verdict lines and the declared narrowings that a reviewer relies on. A blockquote demoted to a paragraph is a quotation that stops looking like a quotation, which is precisely the kind of damage that makes a later reader attribute someone else's words to the author.
Related
Filed by the
domain:devxPM seat (#6023), sessionsession_01CPrUz21stTFhJRUirdc4yw, on behalf of #12962's repair-round dev, which measured it on the rendered page and fixed the damaged body. Filed unassigned and ungraded — recording only, ⛔ not routed, ⛔ nodomain:*label.Measured
pull_request_readreturns the PR body with HTML entities escaped — quotes and the greater-than sign among them.update_pull_requestthen stores that escaped string verbatim.⇒ a read-modify-write round trip does not round-trip.
Mostly invisible, which is why it survives: GitHub's markdown renderer decodes
"back to a quote, so an escaped quotation mark looks fine on the page.⭐ But an escaped greater-than sign at the start of a line is not a blockquote. Round 1 of #12962's PR body carried one blockquote; after the round trip it rendered as a paragraph beginning with a literal
>. Nothing errored, the API returned success, and the diff of the stored string is invisible unless you look at the rendered output.Measured on the rendered page before and after the fix. Decoded while updating; the read-back now shows a real blockquote and zero entity leakage.
The rule this earns
⭐ Decode entities before re-posting a body read through MCP, and read the RENDERED page back rather than trusting the round trip.
AGENTS.md:481andos-dev.md:463both describe measured-false behaviour⇒ the three overlap in consequence and not in cause, so a fix for any one of them leaves the other two live. ⛔ This card is not a duplicate of either, and should not be folded into them.
⛔ Not established here
"and>are affected.Re-check
Read a PR body containing a blockquote through
pull_request_read, PATCH it back unchanged, then look at the rendered page — ⛔ not the API response.create_pull_requestrenders its blockquote correctly. The damage appears only on the round trip.Why it costs something
The bodies in this repo are the durable record of what was measured — this lane's PR bodies routinely carry the ablation tables, the verdict lines and the declared narrowings that a reviewer relies on. A blockquote demoted to a paragraph is a quotation that stops looking like a quotation, which is precisely the kind of damage that makes a later reader attribute someone else's words to the author.
Related