Skip to content

fix(instructor): fail closed on restructured feedback Html readback - #94

Merged
rabesss merged 2 commits into
mainfrom
fix/91-feedback-html-fail-closed
Nov 8, 2026
Merged

rabesss merged 2 commits into
mainfrom
fix/91-feedback-html-fail-closed

Conversation

@rabesss

@rabesss rabesss commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Fixes the P1 from the #90 council (round 5, Terra) that was downgraded to P2 and merged even though the command did not fail closed.

Problem

After the draft-feedback POST, _check_saved_comment checked the readback Html with regexes that accepted <p …> with any attributes, treated </p><p …> as a line break, and treated a raw \n as a line break. So Html: "<p hidden>hello</p>" (or style=/class=, a second paragraph, <p>x\ny</p>, a trailing <p>) with a matching Text reported saved-draft.

Change

  • _html_text now runs a stdlib html.parser.HTMLParser (convert_charrefs=True) and accepts only the structure the command sends: whitespace, then one <p> with no attributes holding only text and attribute-free <br>, then </p>, then whitespace.
  • Allowed reformatting: whitespace outside the paragraph, one line break directly next to <p>/<br>/</p>, entity spelling (&amp;/&#38;/&#x26;, &quot; vs ", …), tag case, and <br>/<br/>/<br />. NBSP counts as a space and outer line breaks are ignored, as before.
  • Anything else (any attribute, another element including <script>/<style>, comments, doctype, CDATA, PIs, a second or unclosed paragraph, </br>, an unfinished trailing tag, text after </p>, or any other raw line break) → mismatch → outcome: "unknown".
  • I checked the matcher on CPython 3.11, 3.12, 3.13 and 3.14 against the edge cases (EOF handling of unfinished tags and comments, bogus comments). All four give the same results.
  • Docs: updated the Readback 1 row and live-check step 5 in docs/assignment-feedback.md.

Tests

  • Rejected even when Text matches: <p hidden>, <p style=…>, <p class=…>, <p id=…>, <br class=…>, span/div/img, <script> and <style> inside or after the paragraph, comments (also unfinished and bogus), doctype, CDATA, PI, a second paragraph (filled, empty or unclosed), <p/>, unclosed, no paragraph, a stray or double </p>, </br>, a trailing <b, text or an entity after </p>, a raw \n inside, two newlines after <br>.
  • Accepted: <br> spellings, tag case, spaces inside tags, outer whitespace, line breaks next to tags (LF and CRLF), entity spellings, raw < text, NBSP, a dropped trailing break.
  • _html_text(_html(t)) round-trips for a set of tricky texts.
  • CLI: <p hidden> with matching Text → exit 1, outcome: unknown, never saved-draft.
  • The old test that accepted paragraphs and attributes-nbsp is replaced; both shapes are now rejected.

Scope

Owner wrap-up: this PR only fixes the P1. The #91 items (cancel-document ids, Ctrl-C before the POST, published-warning ordering, "Nothing was sent." on pre-POST transport errors, broadening _send, the duplicate method check in write(), TOCTOU, live shape) and the round-5 P3s that this change doesn't already fix stay open on #91. The parser fixes two of those round-5 P3s: a raw \n counting as a line break, and an accepted trailing <p>.

Refs #91

Gate (head 68df29e)

  • Round 1 (b1da724): Terra found three false saved-draft shapes: end-tag junk, dropped invalid charrefs, and ignored outer line breaks. The check now validates raw tokens: it checks each tag's spelling, slices the text between tags from the source, decodes it with a strict reference decoder, and compares line breaks exactly.
  • Round 2 (68df29e): Opus high found no P0/P1, and so did Muse Spark xhigh. Terra xhigh raised one P1: zero-padded charrefs over 8 digits give unknown. That fails closed, so it was lowered to P2 and filed in assignment-feedback: follow-ups from #94 reviews (fail-closed edges and tests) #95 with the P3s.
  • Matcher checked by hand on CPython 3.10.17, 3.11.12, 3.12.10, 3.13.3 and 3.14.8. Required CI checks pass. droid-review failed because Droid hit its usage limit; it isn't a required check.

Parse the readback Html with html.parser and accept only the structure the
command sent: one attribute-free <p> holding text and attribute-free <br>
line breaks. Whitespace outside the paragraph, a line break next to a tag,
entity spelling, tag case and <br> spelling are tolerated; attributes such as
hidden/style/class, other elements, comments, a second paragraph or any other
raw line break now report unknown instead of saved-draft.

Refs #91
@pullfrog

pullfrog Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

This run was cancelled before it finished.

Pullfrog  | View workflow run | via Pullfrog

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@factory-droid

factory-droid Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Droid encountered an error —— View job

Droid could not run because a usage limit was reached: You've reached your 5-hour Droid Core usage limit (resets in 3h 0min).
Reload Extra Usage credits or wait for your limits to reset.

@greptile-apps

greptile-apps Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issue was found in the changed code.

Summary

This PR makes feedback readback stricter. _html_text now accepts one attribute-free <p> containing text and attribute-free <br> tags.

  • Draft readback checks accept only the comment’s expected paragraph structure.

Scope acknowledgment: rabesss explicitly deferred the remaining #91 items: cancellation IDs, interrupts before the POST, published-warning ordering, pre-POST transport wording, broadening _send, the duplicate method check in write(), concurrent-write timing, and live response shapes. Other round-5 P3 items remain deferred unless this parser change already fixes them.

Reviews (1) · Last reviewed commit: "fix(instructor): fail closed on restruct..." · Reviewed by Greptile

html.parser drops invalid character references and end-tag junk, and the
old comparison ignored outer line breaks, so <p>he&#1;llo</p>,
<p>hello</p hidden=x>, <p>he</>llo</p> and <p><br>hello<br><br></p> could
still count as saved-draft. Check each tag's raw spelling, slice the text
between tags from the source, decode it with a strict reference decoder,
and compare line breaks exactly. Any parser error now means unknown.
@factory-droid

factory-droid Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Droid encountered an error —— View job

Droid could not run because a usage limit was reached: You've reached your 5-hour Droid Core usage limit (resets in 2h 45min).
Reload Extra Usage credits or wait for your limits to reset.

@rabesss
rabesss merged commit b0eccdf into main Nov 8, 2026
4 of 5 checks passed
@rabesss
rabesss deleted the fix/91-feedback-html-fail-closed branch November 8, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant