Files

4.3 KiB

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.