Skip to content

Sort the evidence before cutting it, and stop the Markdown carrying live HTML - #52

Merged
peopleworks merged 1 commit into
mainfrom
report-evidence-by-weight
Aug 10, 2026
Merged

Sort the evidence before cutting it, and stop the Markdown carrying live HTML#52
peopleworks merged 1 commit into
mainfrom
report-evidence-by-weight

Conversation

@peopleworks

Copy link
Copy Markdown
Owner

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

EvidenceReport took 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:202 took result.Signals.Take(40) with no ordering, twelve lines above :322, where the folder table already sorted OrderByDescending(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:

list before now
signals position, silent about order weight desc, and the page says so
citations position, omission not stated at all position, omission stated
characters position IsStrong first, then position
observations rule-id order, omission not stated count desc, omission stated
folder table every file, sorted unchanged — deliberately exempt

Ties 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 delves prints 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. ToMarkdown passed raw HTML through

ToHtml escaped 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.

  • The escape is a backslash, not an entity, so the character survives one escaping and exactly one. The HTML path resolves it before escaping, and the matched text is reproduced as the document wrote it — the claim this product rests on. An entity would have double-escaped into &amp;lt;img on the HTML page.
  • The backslash is escaped first. Escaping only the bracket is escaping that one extra character defeats: a document already containing \< becomes \<, which Markdown reads as an escaped backslash and then a live tag. There is a test for exactly this.
  • Same path now applied to three things that were bypassing it: rule-pack summaries and rule ids (pack.Text(...) — JSON anyone can PR), and the caller's title (every host builds it from a filename).
  • It also fixes a visible bug: a pipe in a citation message used to reach the HTML page as \|, because SplitRow resolved 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 strip javascript: URLs. Noting it rather than doing it quietly.

Verification

  • 352 tests green across the solution.
  • Seven new tests, and all seven fail when the source is reverted to origin/main — checked by stashing only src/SignsOfAI.Core/Reporting and re-running.
  • Read the generated report, not only the assertions: that is where the fourteen-delves question came from, and it is not visible in any test.
  • Two new message keys, translated into Spanish in the same commit with fresh sourceHash pins, 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

… 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
peopleworks merged commit 46fa5b2 into main Aug 10, 2026
4 checks passed
peopleworks added a commit that referenced this pull request Aug 10, 2026
The committee's findings, which arrived after #52 was merged
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