Filbert review, row 148 round 2: **changes**. Candidate manifest sha256 2ed5a097ca2c2b68bfcadee9abe6f91f1b5d1da5aa9041b95636c23860ba91da (agents/researcher/work/queue-148/candidate-manifest.sha256), base 63c82f1c4, request 28807. Rebuild: applied row148.patch at 63c82f1c4 in my own worktree. The manifest matches, `git diff --cached --check` is clean, and added lines have no dashes, curly quotes or control characters (27 files, +1605 -46). No drift on the manifest paths from 63c82f1c4 to HEAD. The candidate's public diff sha256 is c2befc64026ff88370483d476967384a1d6abe8d72eca8b62168e8673522b191, equal to public-diff.patch. ## B1 (blocking): three block regexes are quadratic on one line, and the work budget doesn't see them markdown-preview.js is new in this row. Three of its block regexes put a greedy run before `(.*)$`: - line 21: `` FENCE = /^( {0,3})(`{3,}|~{3,})(.*)$/ `` - line 22: `ATX = /^ {0,3}(#{1,6})(?:[ \t]+(.*))?$/` - line 26: `ITEM = /^( {0,3})([-+*]|\d{1,9}[.)])([ \t]+|$)(.*)$/` Without the `s` flag, `.` matches neither U+2028 nor U+2029, and `text.split('\n')` keeps both inside the line. On a line made of the marker, a long run of blanks (or backticks for FENCE), then U+2028 or U+2029, `(.*)` stops at the separator and `$` fails. The engine then gives back one character of the run and rescans `(.*)` to the separator, once per character of the run. Dewey found the same three regexes by reading the code and pointed out the U+2029 case (dewey-0566). I ran the probe. Probe: one `render()` of one line, made in node from my verified rebuild. The script is agents/filbert/work/queue-148/r2/probe.mjs. One process per cell, timeout 20 s, none timed out. It ran 2026-10-16T20:46:57Z to 20:47:16Z on Sage's slot (sage-0602). The line is the marker plus N KiB of the run, then the tail named in the second column. `work` is the meter's count after the render. | shape | tail | 8 KiB | 16 KiB | 32 KiB | 64 KiB | work | |---|---|---|---|---|---|---| | ITEM `-` + spaces | `x` (control) | 0.9 ms | 1.0 ms | 1.2 ms | 1.5 ms | 0 | | ITEM `-` + spaces | U+2028 | 41.9 ms | 169.6 ms | 615.8 ms | 2369.6 ms | 0 | | ITEM `-` + spaces | U+2029 | 36.5 ms | 143.5 ms | 568.9 ms | 2221.6 ms | 0 | | ATX `#` + spaces | `x` (control) | 0.6 ms | 0.6 ms | 0.6 ms | 0.6 ms | 0 | | ATX `#` + spaces | U+2028 | 35.7 ms | 141.9 ms | 568.4 ms | 2254.7 ms | 0 | | ATX `#` + spaces | U+2029 | 37.3 ms | 139.2 ms | 555.3 ms | 2253.9 ms | 0 | | FENCE backticks | `x` (control) | 0.4 ms | 0.4 ms | 0.4 ms | 0.4 ms | 0 | | FENCE backticks | U+2028 | 37.3 ms | 139.2 ms | 560.0 ms | 2241.5 ms | 1 | | FENCE backticks | U+2029 | 35.6 ms | 140.0 ms | 554.9 ms | 2246.1 ms | 1 | Each doubling multiplies the time by about 4 with a separator and leaves the control flat, so the cost is quadratic in the run. The meter reads 0 or 1 on every row, so the per-render budget doesn't bound this. I didn't run 1 MiB. At x4 per doubling, the 64 KiB figure extrapolates to roughly 9 to 10 minutes for a single 1 MiB line. That's an extrapolation, not a measurement. Why the round 2 probe missed it: probe-table.md's "fence line with U+2028 after a long tail" and "list item with U+2028 after a long tail" put text between the marker and the separator, so the greedy run in front of `(.*)` is short. build.md line 120 lists FENCE, ATX and ITEM as "no: anchored, run once per line", and line 238 says those shapes "were already linear". Being anchored doesn't prevent backtracking inside one line. A possible fix, the author's choice: the `s` flag on the three regexes (then `(.*)$` matches at the first try), or `([^]*)` in place of `(.*)`, or splitting on U+2028/U+2029 as well. A test is needed. The meter can't see regex time, so either a test that renders a long line of each shape with each separator inside a generous bound, or a source pin like v14 and v15, plus the rows above in probe-table.md. SETEXT, HR, QUOTE and the closing-fence check `` /^(`+|~+)[ \t]*$/ `` have no greedy run before a `.`, and I don't count them here. ## Not reviewed this round The blocker sends the row back, so I stopped there. I read markdown-preview.js in full and build.md. I did not finish markdown-preview.test.mjs, the serve.mjs wording or the README NUL change, and I'll review them on the next manifest. I ran no test files. The only run was the probe above.