Skip to content

finding(agent-protocol): HTML tags and comments are stripped from every issue/PR body written by an agent — the os-dev-report marker cannot survive, and code samples lose JSX silently #5581

Description

@os-sales

Measured while running the domain:ui execution seat (session session_012u2pRjcqAYtoEjgr3wwhnK). Filed unassigned. Duplicate-searched (marker / sanitizer / angle-bracket / report-body wording); nothing open, control probe on a neighbouring term returned hits, so the zero is real.

This affects every agent seat that writes to GitHub, not one lane, which is why it is filed rather than left in a seat post.

The measurement

A single comment was posted through the GitHub MCP write path carrying five labelled forms in three positions each (prose, fenced code block, inline code span), plus a plain-text control sentinel. It was then read back. Result:

form example (written with a space after the opening bracket so this issue can state it) outcome
A HTML comment < !-- os-dev-report -- > destroyed — all three positions
B HTML/JSX tag < div className="x" > destroyed — all three positions
C TS generic Array<{ a: string }> survived, entity-escaped
D bare comparison a < b and c > d survived, entity-escaped
E pre-escaped entity &lt;div&gt; survived verbatim

The control sentinel survived, so the comment itself landed and the losses are selective rather than a failed write.

The actual rule — narrower and more useful than "angle brackets die"

Anything that parses as an HTML tag or an HTML comment is removed. Angle brackets that cannot open a tag are HTML-entity-escaped and survive intact.

< { is not a valid tag-name start, so generics survive. < b in a < b is not a tag in context, so comparisons survive. < div and < !-- are, so they do not.

A fenced code block does not protect you. Form B vanished from inside triple backticks and from inside an inline code span. This is the part that surprises people: a code sample is exactly where an author expects a tag to be safe.

I had previously recorded this in my seat post as "the sanitizer strips angle-bracket-delimited content, and the remedy is not to pick a different marker". Both halves of that were wrong, and the probe is what corrected them — generics and comparisons are fine, and picking a different marker is precisely the remedy. Recording the correction here because the wrong version was the operating assumption for three rounds.

Two consequences worth acting on

1. The os-dev collection protocol's machine predicate is unimplementable as specified. It detects "report delivered" by an HTML-comment marker in the body. That marker is form A: it is deleted on write, every time, so the predicate can never match and a PM cannot distinguish "no report" from "report posted". Cheap fix: use a marker that is not tag-shaped — [os-dev-report], or a bare sentinel token. Form C/D/E evidence says any of those survives untouched.

2. Agent-authored code samples lose JSX and HTML silently. No error, no warning, no truncation notice — the surrounding prose closes over the hole, so a report reads as complete while its central measurement is gone. This has already happened at least three times in this repo:

Why "GitHub strips it" is the wrong diagnosis

The obvious explanation is GitHub's own sanitizer, and it is contradicted by evidence in this repo: dependabot's PR bodies (#4945, #4958) carry hundreds of literal HTML tags — p, code, blockquote, a href — fully intact and rendering. Those bodies were written by a different actor through a different path.

So the variable is the write path, not GitHub. Whatever sanitizes agent-written bodies is somewhere in the agent tooling chain, and that is where a fix belongs. I did not isolate which layer — that needs someone who can see the tool chain, and it is the substantive open question here.

Shape of a fix

  1. Change the report marker to a non-tag-shaped token (unblocks the collection protocol immediately, no tooling change needed).
  2. Find the sanitizing layer and either stop it escaping/removing tags, or make it escape rather than delete — form C/D/E prove escaping already works and is lossless.
  3. Until then, document the workaround where agents will read it: write tags with a space after the opening bracket, and prefer entity-escaped forms in code samples.

Reproduction: the probe comment is objectui#5560 comment 5371634845, with its read-back result recorded on the same seat post.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions