Skip to content

fix(faq-bot): bound regex matching to the first 150 characters - #113

Merged
rmyndharis merged 1 commit into
mainfrom
fix/faq-bot-lower-regex-input-cap
Sep 6, 2026
Merged

fix(faq-bot): bound regex matching to the first 150 characters#113
rmyndharis merged 1 commit into
mainfrom
fix/faq-bot-lower-regex-input-cap

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Closes #112.

The parse-time screen reads the pattern text. That works for the exponential shapes it can recognise syntactically, a quantifier on a group that holds one and an ambiguous repeated alternation, but not for the polynomial ones, where cost depends on how many ways an input splits between adjacent quantifiers. Those are not reliably visible in the text: three separate attempts to make them so each closed one family of patterns while opening another, and one of them shipped a regression that refused ordinary rules such as .*(\d+).*.

Bounding the input needs no model at all. A polynomial blow-up costs about n^3, so the cap sets the ceiling directly, whatever the pattern looks like and including shapes nobody has found yet.

Measured

Worst shape that gets past the screen, ((a|b)*(a|b)*)(a|b)*$, against alternating input that fails on the last character:

Cap This machine At half this speed At a fifth
1000 over 8 s
300 5.8 s 11.6 s 28.9 s
250 2.8 s 5.7 s 14.2 s
150 362 ms 723 ms 1.8 s

The host aborts a hook at 5 s and cannot interrupt a running regex, so the ceiling has to hold on a host slower than the one that measured it. 250 does not.

A fuzz over 106 patterns the screen accepts found one still above 5 s at 1000 characters and none at 250, so the cap also covers what the screen misses beyond the eight known cases in #112.

Why not a smarter screen

Five independent designs were implemented and measured against a differential gate, reported in #112. All five closed the known holes and all five opened new ones, in four separate families. The pattern across them is that a text-level model of backtracking cost is at its limit, not that the right rule had not been found.

The two controls now cover what each is actually good at. Exponential shapes stay fatal at any input length, so only the screen can stop them, and they are the ones it recognises reliably. Polynomial shapes are the opposite: hard to see in the text, bounded by the cap.

Cost

A regex rule no longer matches text past the first 150 characters. That is a predictable limit, documented in the plugin README, rather than a rule that silently stops answering.

contains and exact cannot backtrack and are not capped, so they still see the whole message. contains remains the right mode for matching a mention anywhere in a long one.

Verification

npm run typecheck, npm test (660 pass), npm run catalog:check, npm run loader:check.

Two new tests pin the cap and fail if it is raised, verified at 300 and at 1000. The second builds its input to be adversarial inside the window, since the cap slices from the front and a failing tail appended to a long message is simply cut off, which made an earlier version of that test pass at any cap.

faq-bot 0.2.10.

The parse-time screen reads the pattern text. That works for the
exponential shapes it can recognise syntactically, a quantifier on a
group that holds one and an ambiguous repeated alternation, but not for
the polynomial ones, where the cost depends on how many ways an input
splits between adjacent quantifiers. Those are not reliably visible in
the text: three separate attempts to make them so each closed one family
of patterns while opening another.

Bounding the input needs no model at all. A polynomial blow-up costs
about n^3, so the cap sets the ceiling directly, whatever the pattern
looks like and including shapes nobody has found yet.

Measured on the worst shape that gets past the screen,
`((a|b)*(a|b)*)(a|b)*$` against alternating input that fails on the last
character: 1000 characters runs past 8 s, 300 takes 5.8 s, 250 takes
2.8 s, 150 takes 362 ms. The host aborts a hook at 5 s and cannot
interrupt a running regex, so 150 is chosen to hold on a host several
times slower than the machine that measured it. 250 would not: it is
2.8 s here and 5.7 s at half this speed.

A fuzz over 106 patterns the screen accepts found one that still exceeds
5 s at 1000 characters and none at 250, so the cap also covers what the
screen misses beyond the known cases.

Only `regex` rules are affected. `contains` and `exact` cannot backtrack
and are not capped, so they still see the whole message, and `contains`
remains the right mode for matching a mention anywhere in a long one.
The cost is that a regex rule no longer matches text past the first 150
characters, which is a predictable limit rather than a rule that stops
answering without saying so.
@rmyndharis
rmyndharis merged commit 79eab8b into main Sep 6, 2026
1 check passed
@rmyndharis
rmyndharis deleted the fix/faq-bot-lower-regex-input-cap branch September 6, 2026 02:57
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.

faq-bot: the regex screen misses adjacent quantifiers spelled with groups or classes

1 participant