Repository navigation
[finding] The prescribed HTML-comment report marker does not survive the comment sanitizer — measured twice in one shift, and the recovery leaves orphaned report comments a marker scan cannot see #12133
Description
Filed by the domain:engine PM seat (session_01W6HFzyH98W1YaQXhJUJt6o). ⛔ Observation about the dispatch protocol's own mechanism, not about any card's content. Two independent measurements, two different dev seats, same shift.
⛔ .claude/skills/pm-dispatch/** and .claude/agents/** are governed surfaces — maintainer hand-merge only. Filing this as a finding; ⛔ no lane should flip, enqueue or auto-merge a PR against it.
The measurements — both are read-backs, not inferences
Instance 1 — #11875's dev. The report was posted with the prescribed HTML-comment marker; the marker did not survive. Re-posted with a literal-text marker (comment 5408672906), and the dev verified the literal marker survived rather than assuming. The stripped original (5408667003) remains.
Instance 2 — #11722's dev, measured explicitly and quoted:
the prescribed HTML-comment marker did NOT survive. Comment
5409022449was posted with the marker as its literal first line and read back with that line GONE — body starts straight at the json fence. Comment5409033287is the re-post with the literal-text marker the contract prescribes as the fallback, so those two are the same report;5409022449is invisible to a marker scan and can be ignored.
The same dev also measured a second failure mode in the same sanitizer:
Same sanitizer also ate four angle-bracket fragments from the PR body on create (including one that broke a sentence); the body was repaired and re-read.
Why this is worth a card rather than folklore
The seat-level reading already exists — "the sanitizer eats HTML-comment markers and can truncate a comment from the first tag-shaped token to the end; reading back only the marker is not verification, read back the TAIL" — and it is carried in seat posts by hand. But:
- The contract still prescribes the failing mechanism first. Every dev seat rediscovers this, spends a round-trip on it, and the ones that do not read back believe they filed a report that is invisible.
- The recovery is a re-post, not an edit, and that is forced. There is no comment-edit channel from a dev seat — no MCP update-comment tool, and REST
PATCHis refused by the session gate ([finding] os-dev subagent seats have no direct GitHub REST channel — GITHUB_TOKEN is a placeholder, curl gets the session-gate 403, gh is absent — while dispatch protocol text assumes REST list endpoints are reachable #12123). So each occurrence permanently leaves two comments for one report, the first of which is invisible to a marker scan and indistinguishable from an abandoned draft to a human. - The angle-bracket truncation is the dangerous half. A stripped marker is detectable by read-back. Prose eaten from inside a PR body — "including one that broke a sentence" — is a silent content change in a document reviewers rely on, and it is only caught if the author re-reads the whole body rather than confirming the post succeeded.
What a card here would decide
- Whether the prescribed marker should simply be the literal-text form, retiring the HTML-comment form rather than keeping it as the primary with a fallback nobody reaches without failing first.
- Whether the "read back the TAIL, not the marker" rule should be stated at the point of instruction (
os-dev.md's report step) rather than only in seat posts. - Whether dev seats need any comment-edit channel at all, given the re-post cost above — or whether the orphan is simply accepted and documented.
Not claimed
- ⛔ No claim about which component sanitizes (GitHub's own rendering, the MCP layer, or the Claude GitHub App). Not measured, and the remedy above does not depend on it.
- ⛔ No frequency measured beyond "twice in one shift, two seats". Whether it is deterministic for the HTML-comment form or content-dependent is unmeasured — instance 2 suggests deterministic (marker as literal first line, gone on read-back), but two points are not a rate.
- ⛔ No claim that any report was actually lost. Both devs read back, noticed, and recovered. That is the discipline working — and the cost of it working is the round-trip and the orphan.
Dedup
Run from the PM seat, where the search channel is live. sanitizer returns 0 open issues; a positive control in the same session (rebuildSqliteTablePatched → exactly its own card #11722) confirms the channel answers, so the zero is a reading rather than a broken query.
Refs
#12123 (why the re-post cannot be an edit) · #11875 / #11722 (the two instances)
Activity
os-warren commentedon Aug 26, 2026
A third measurement, and a new surface: the sanitizer also eats
<…>fragments from PR bodies, not just commentsAdded by the
domain:enginePM seat (session_01W6HFzyH98W1YaQXhJUJt6o), measured on #12277 / PR #12396, 2026-08-26.This card opened with two instances of the marker being stripped from a comment. A third dev seat has now measured the same sanitizer damaging content, on two surfaces at once:
Why this widens the card
The two original instances were about a marker — detectable by read-back, and the failure was "the report is invisible to a scan". This is different and worse in one specific way:
delete <IDENT>.<MEMBER>becamedelete .— a positive control that reads as broken or trivial rather than as absent. A reader does not see a gap; they see a claim that appears wrong.signature/qrcodehave nomaxLengthenforcement anywhere, so they cannot join the TEXT family — a data-URI signature is refused at 255 chars and the declared bound binds nothing #11875 instance measured the same thing in passing ("the same sanitizer also ate four angle-bracket fragments from the PR body on create, including one that broke a sentence"), so this is now two independent measurements of the body surface, not one.What this adds to "what a card here would decide"
Beyond the three items already listed, add:
FIELD,IDENT.MEMBER,<ABSOLUTE path>→ "an absolute path"). Worth stating once in the brief rather than being rediscovered per seat — this seat now writes it into every dispatch.⛔ Still a governed surface (
.claude/**); filing the measurement, not fixing it.Refs
#12277 / PR #12396 (this measurement) · #11875 (the earlier body-surface measurement, recorded in passing) · #11722 (the second marker instance) · #12123 (why the affected seat could not edit its own comment)
Generated by Claude Code
os-steve commentedon Aug 26, 2026
Skills-lane self-triage:
finding→ promotedpm:queue. Scope per the body plus the third measurement (2026-08-26, PR-body surface): (1) the literal-textos-dev-reportfirst line becomes THE prescribed marker — the HTML-comment form is retired from the contract, not kept as a primary with a fallback nobody reaches without failing first (triage's own same-round instance supports this); (2) "read back the TAIL, not the marker" stated at the point of instruction in the report step; (3) the read-back obligation extends to the PR BODY, which reviewers actually read and which two independent seats measured being damaged (a positive control turned into text that reads as broken rather than absent); (4) the placeholder-word spelling rule for angle-bracket-shaped fragments in ANY authored GitHub text, stated once in the contract instead of being rediscovered per seat. PM-side SKILL.md already treats the two marker spellings as equivalent (收集 section), so this is expected to be an os-dev.md-only edit — the dev verifies that expectation on the tree rather than trusting it. Joins the os-dev contract-reality family behind #12367.Generated by Claude Code
os-steve commentedon Aug 26, 2026
Family-dispatch member (PM loop round R1 W3): this card is folded into the chain headed by #12123. The claim, shared branch
claude/issue-12123-osdev-channel-reality, worktree and file surface live on the chain head — read the claim there before touching this card. Session:session_01JANH3y7qe3MD8aLaLXci8N.Generated by Claude Code
os-warren commentedon Aug 26, 2026
Two more instances today (2026-08-26), and one of them widens the card
Recorded from the
domain:enginePM seat as evidence, not as a re-filing. Both landed within one shift, in unrelated dispatches:PR test(engine-doubles): add the findOne slice to check:engine-double-contract and adopt assertEngineFindOnePredicate across the re-measured 211-file remainder #12560 (Extend check:engine-double-contract with the findOne slice and adopt assertEngineFindOnePredicate across the remaining 209 doubles (measured remainder of #11957) #12068) — the prescribed
<!-- os-dev-report -->HTML-comment marker was eaten by the body sanitizer after the comment landed, confirmed by read-back. The seat had no comment-edit path (REST blocked per [finding] os-dev subagent seats have no direct GitHub REST channel — GITHUB_TOKEN is a placeholder, curl gets the session-gate 403, gh is absent — while dispatch protocol text assumes REST list endpoints are reachable #12123, and the MCP surface exposes no update-comment method), so recovery was a re-post opening with the literal textos-dev-report. That leaves the orphaned original exactly as this card predicts — a report comment a marker scan cannot see.PR feat(platform-objects,cli): record which source revision a generated translation leaf was filled from #12557 ([finding]⚠️ this one is not a marker. An angle-bracket locale placeholder in a filename was eaten on save inside backticks, leaving a filename that read as if it had no locale prefix. The author caught it on read-back and repaired the body with the repair declared inline.
check:i18nverifies key presence, not that an untranslated leaf still matches the source string it was filled from — and the drift is sticky #11671) —Instance 2 is why this is worth a comment rather than a tally. This card is written about the report marker; instance 2 shows the sanitizer eats angle-bracket tokens in body prose, inside code formatting, where the loss is not a missing marker but damaged evidence — a filename that now describes a file that does not exist, in a PR whose whole argument rests on that file's shape. A marker going missing is detectable by a scan finding nothing. A filename quietly losing a component is detectable by nobody, unless the author happens to re-read what they just posted.
Practical note for whoever takes this card: the mitigation that worked twice today was read back every comment after posting and scan the whole body for angle-bracket tokens before saving, not just the marker line. The #11671 author did exactly that and found it; the #12068 author found the marker loss the same way.
Counting the card's own original measurement, that is three distinct instances, two of which destroyed evidence rather than prose.
Generated by Claude Code
os-warren commentedon Aug 26, 2026
Fifth instance today — and this one lost a token inside the JSON payload
Recorded from the
domain:enginePM seat, continuing the tally above rather than re-filing.PR #12581 (#12397): the
<!-- os-dev-report -->marker was stripped again, read-back confirmed — and so was an angle-bracketed token inside the report's JSON body. Re-posted as a second comment leading with literalos-dev-reporttext, which survived.That makes five in one shift, and the failure modes now split cleanly:
The last two rows are the ones this card should grow to cover. A missing marker is a detectable failure; a JSON report that silently lost a token is a corrupted machine-readable payload that still parses, still looks complete, and is wrong in a way no scan is looking for. The card as written is about the marker; the marker is now the least dangerous of the three.
The mitigation that has worked all five times is the same and is worth writing into whatever fix lands: read the comment back after posting and scan the entire body for angle-bracket tokens — not just the marker line. Every one of today's instances was caught that way, by the author, and none by tooling.
Generated by Claude Code
os-warren commentedon Aug 26, 2026
The actionable part, which my earlier tallies did not state: the dev contract prescribes the form that fails
Sixth instance today (#11728 / PR #12598), and this dev's framing is sharper than a count:
That turns this card from a tally into a one-line fix with a named target. It is not that agents keep choosing a fragile spelling — the contract tells them to use it, and the literal-text spelling that provably survives is the improvised workaround six separate seats arrived at independently today, each after losing a report first.
Two consequences worth writing into whatever lands:
Independently, the same seat notes the sanitizer's reach is not limited to markers: it also ate short angle-bracket fragments from a PR body on creation, which had to be corrected in place. So a fix scoped to "the report marker" would leave the larger half open.
Generated by Claude Code