Sort the evidence before cutting it, and stop the Markdown carrying live HTML - #52
Merged
Merged
Conversation
… first Two of the four defects in #37, which share a cause: the report cut its lists without first deciding what deserved to be on the page. The analyser returns findings in the order they occur in the text, because that is what highlighting and the rewriter need. The report printed them that way and stopped at forty. In a long document that spent the whole list on weak hits in the opening pages while the finding that did most to produce the headline number sat in the last paragraph, omitted — so the reader was handed a score the visible evidence could not account for, in the one document this project builds for a room where somebody is judged. Every list that gets cut is now sorted before it is cut, and every one of them says how many it left out. The citation list said nothing at all, alone among the four: a reader counting contradictions against the summary above them would have found the page contradicting itself about a document accused of contradicting itself. Characters put the kinds the scanner already calls strong ahead of the two hundred soft hyphens Word inserts unprompted. Observations lead with the rule used thirty times rather than the twenty that fired once. The folder table stays exempt and still prints every file. Ties among signals go to the rule that has appeared least, because sixteen English rules share the weight 3.5 and one rule's run is worth less to the reader than one occurrence of each. It reorders strictly within equal weight, so the page keeps the right to say that nothing omitted outweighs what is shown. Second defect: ToMarkdown passed raw HTML through. ToHtml escaped on its way out, so the HTML page was never at risk — but Markdown is the form documented here for pasting into an LMS comment box or a GitHub issue, and both render embedded HTML. A document containing <img src=x onerror=…> reached them intact, and the person pasting it is a teacher who has been told the report is the safe thing to forward. The escape is a backslash rather than an entity so the character survives one escaping and exactly one: the HTML path now resolves it before escaping, and the matched text is reproduced as the document wrote it, which is the claim this product rests on. The backslash itself is escaped first, because otherwise a document already containing \< defeats the escape with one extra character. Rule-pack summaries and ids, and the caller's title, go through the same path — all three are text this report did not write. Six new tests, each failing on the parent commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
peopleworks
added a commit
that referenced
this pull request
Aug 10, 2026
The committee's findings, which arrived after #52 was merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the first two items of #37. The other two — nesting emphasis markers, and building the HTML from the model instead of re-parsing Markdown — are untouched, as are the two smaller items at the bottom of that issue.
1. Truncation dropped the evidence that produced the score
EvidenceReporttook the first 40 findings and the analyser orders findings by position in the text, not by weight, while the score sums all of them. A long document could spend the whole list on weak hits in its opening pages while the strongest signal sat in the last paragraph, cut. The reader got a score the visible evidence could not account for.Verified before touching anything:
EvidenceReport.cs:202tookresult.Signals.Take(40)with no ordering, twelve lines above:322, where the folder table already sortedOrderByDescending(e => e.Score). The report knew how to sort and did not do it where it mattered.Now, every list that gets cut is sorted first and every one of them states what it left out:
IsStrongfirst, then positionTies among signals go to the rule that has appeared least: sixteen English rules share the weight 3.5, and in a tie one occurrence of each is worth more to the reader than one rule's run. It reorders strictly within equal weight, so the page keeps the right to say nothing omitted outweighs what is shown.
Left for the issue, deliberately. A rule that genuinely outweighs the rest still fills the list with its own repeats — a document with fourteen
delvesprints fourteen rows. That is the honest picture of where such a document's number comes from, and collapsing them into a count, as the observations section does, is a question about the section's shape rather than about which evidence it drops. Worth deciding, not worth deciding here.2.
ToMarkdownpassed raw HTML throughToHtmlescaped on its way out, so the HTML page was never at risk. Markdown is the form this file documents for pasting into an LMS comment box or a GitHub issue, and both render embedded HTML.<img src=x onerror=…>reached them intact.&lt;imgon the HTML page.\<becomes\<, which Markdown reads as an escaped backslash and then a live tag. There is a test for exactly this.pack.Text(...)— JSON anyone can PR), and the caller's title (every host builds it from a filename).\|, becauseSplitRowresolved table escapes and nothing resolved them anywhere else.Considered and declined: escaping
[and]against link injection. Academic citations are full of brackets and the raw Markdown a teacher reads would become\[1\]; renderers that matter stripjavascript:URLs. Noting it rather than doing it quietly.Verification
origin/main— checked by stashing onlysrc/SignsOfAI.Core/Reportingand re-running.delvesquestion came from, and it is not visible in any test.sourceHashpins, plus a test that the Spanish reader gets the ordering line in Spanish — a stale pin drops it back to English without saying anything is wrong with it.Still open, and Pedro's call
The house rule is that the committee reviews anything that changes behaviour. This does. It has not been reviewed by Fable or Codex yet.
🤖 Generated with Claude Code