Collection bulk actions and regex safety
Desktop Material gives collection search fields one shared fuzzy, substring, and regular-expression contract. A tracked registry maps each real search input to a stable surface ID and its regex builder. A second registry records which collection managers support reviewed bulk work and which must remain one-at-a-time because their topology or recovery requirements differ per item.
Behavior and configuration
The mode control cycles through fuzzy, contiguous substring, and safe RE2 regular-expression matching. Substring and regex modes can match case. The Regex Builder starts from the surface's current query and case mode, offers RE2-compatible token categories, a raw pattern editor, the supported ignore-case flag, a live tester seeded with up to 50 visible items, and an explanatory guide. Applying a valid pattern switches that surface to regex mode and applies the exact same case choice that the preview used.
RE2 supports literals, classes, anchors, quantifiers, capture and non-capture groups, named captures, and alternation. It deliberately rejects lookaround and backreferences so evaluation remains linear-time. The tester highlights matches, counts a bounded set, and shows numbered and named captures from the first match; empty and unmatched captures are identified explicitly. Each sample row is evaluated as an independent originating-list candidate, so anchors retain per-item meaning and a pattern can never bridge two rows. The global match/capture-work budget is shared across every row. Physical line breaks therefore separate tester candidates; the palette intentionally omits a newline chip that could not match in this row-oriented preview.
The Build/guide and token-category tablists use one Tab stop each.
The Build/guide row uses Left/Right, while the token-category rail also
accepts Up/Down for its desktop vertical layout and retains Left/Right
for its compact grid. Home and End jump to either edge; selection and
focus move together while every aria-controls target stays
mounted and correctly hidden when inactive.
Implemented bulk surfaces retain operation-specific review. Examples include rerunning completed or cancelling active workflow runs, deleting Actions caches by key and ref, deleting exact branch tips, syncing selected repositories, notification actions, publishing or deleting exact releases, and reviewed tag updates. Submodules, subtrees, stashes, and worktrees are explicit exclusions because a broad action would bypass their per-item topology, dirty-state, ordering, or conflict review.
The repository picker is a second repository bulk surface, separate
from the Sync repositories dialog. Its selection is bounded to the
filter-visible saved rows; cloning and submodule rows are excluded
because their ids are temporary or cannot be removed at all. Fetch and
pull are submitted one reviewed single-repository batch at a time so the
store revalidates every id and keeps its per-repository pull review,
cancellation only takes effect between repositories, and removing
repositories from the list is confirmation gated and has no access to
the move-to-trash or force-delete paths. See
docs/features/repository-management/repository-list-bulk-actions.md.
Persistence
Each registered list persists only its selected match mode in local UI storage under its stable surface ID. Case sensitivity, active filter chips, current query, Regex Builder draft, tester sample, and dialog position remain transient component state. Bulk actions are explicit user-reviewed requests rather than a persisted schedule; their owning surface controls any progress or result retention.
Failure modes and recovery
An invalid or unsupported regex, or a pattern longer than 1,000 characters, never crashes the collection. Matching returns the unfiltered candidates with a localized regex error while the user repairs the expression. An input-size or aggregate-size limit instead fails closed with no matches, so a compact surface that does not render the error cannot silently show every item as though its filter were active; the Actions surface renders that evaluation error directly. The Regex Builder marks an invalid draft, connects the error to the raw editor, and disables Apply. Its status chip stays concise, wraps safely, and points to one detailed alert rather than repeating a long engine message through multiple live regions. Each candidate/test sample is capped at 100,000 UTF-16 code units, one list evaluation is capped at 1,000,000 total code units, match enumeration stops at 5,000, and matcher work is additionally capped at 50,000 capture-group/match slots. Only 24 first-match capture previews are retained, each with at most 120 UTF-16 code units. Zero-width matches advance in the RE2 matcher and are counted without creating empty highlight boxes.
Diff search keeps literal/fuzzy search available for large minified lines. Raw regex mode applies the input caps above, while every mode shares one 5,000-hit operation budget. Zero-width and capture-heavy matches consume that same budget across every line and side-by-side column even when they cannot create a visible highlight. The first bounded results remain navigable and the live region explicitly announces truncation; a limit is never reported as a false "No results" outcome. An invalid regex submission clears prior highlights instead of leaving stale results beneath the error.
Notification automation rules saved under the former ECMAScript dialect remain disarmed after load. A lookaround or backreference rule is visibly marked as needing review and cannot be armed until the user edits it into safe RE2. Valid leading and trailing whitespace is preserved when a rule is saved. The automation-list search exposes the same localized regex error instead of silently showing an unfiltered list. Duplicate IDs in an imported rules file are repaired deterministically before id-keyed toggle or remove actions can reach the model.
Bulk eligibility is rechecked by the owning operation. Ineligible, stale, or failed items are reported according to that feature's result contract instead of allowing one broad UI selection to bypass its review boundary. Managers whose safe common contract is not established remain excluded from bulk work.
Security considerations
User-authored app patterns are compiled only through the pure-JavaScript RE2 adapter against already loaded display strings; the shared matcher does not invoke a shell or provider. Linear-time evaluation plus pattern, input, aggregate, match, and capture bounds prevent regex denial of service in list filters, diff search/highlighting, Actions run filtering, and notification-automation title rules. Capture-heavy expressions receive a compositional work budget rather than multiplying the independent group and match limits. Stable registry IDs make an unreviewed new search field fail the source audit until it adopts the same invalid-regex and builder behavior.
The published documentation site is a static web surface and keeps
its existing ECMAScript dialect and supported flag set. Both of its
pattern surfaces — the documentation hub at /docs/ and the
full-text search page at /docs/search.html — share one
evaluator and one deadline owner. A reader's pattern never runs on the
page thread: a fresh same-origin Web Worker
(docs-hub-regex-worker.js) evaluates each bounded request,
the shared runner in docs-regex-job.js terminates that
worker at a hard 750 ms deadline from the page, and worker
unavailability fails closed rather than falling back to the UI thread.
Plain text is the default on both surfaces and is matched by bounded
substring scans that compile nothing at all; a regular expression is
treated as a pattern only after the reader opts in. Worker responses
retain at most 24 bounded captures from the first match and bounded
match previews, and the full-text scan returns match offsets
into a corpus the page already holds rather than slices of it,
preventing structured-clone amplification. Patterns are capped at 512
characters; the search corpus is capped at 200,000 characters per page,
2,000 pages, and 12,000,000 characters in total, with at most 500
matches counted per page. No app or documentation search transmits or
persists a pattern or test sample.
Bulk operations pass operation-specific bounded identities, such as run IDs, repository IDs, release fingerprints, or exact branch tips. Destructive surfaces retain confirmation and fresh-state validation. Explicit exclusions prevent a generic "apply all" path from weakening a safer individual workflow.
Verification
collection-surface-registry-test.ts inventories native
and shared search inputs, proves one-to-one control and Regex Builder
bindings, checks invalid regex passthrough, and requires every audited
bulk manager to be implemented or explicitly excluded with a safety
rationale. filter-mode-surfaces-test.tsx and
diff-search-input-test.tsx cover mode controls, case
behavior, and builder application, including focus moving into the diff
search's portalled builder. safe-regex-test.ts,
match-with-mode-test.ts,
notification-automation-test.ts, and
regex-test-area-test.tsx cover catastrophic shapes,
invalid/unsupported patterns, Unicode indices, multiline input,
zero-width matches, captures, case behavior, every bound, and the
audited user-pattern call sites. The stress matrix includes 500 capture
groups across a 100,000-code-unit sample, dense diff matches, legacy
notification rules, and leading/trailing pattern whitespace.
regex-builder-v2-style-test.ts and
floating-surface-style-test.ts guard the builder's
accessible views and compact-window reachability.
docs-hub-regex-worker-test.mjs proves bounded
search/highlight results, flags, captures, Unicode zero-width
advancement, invalid input, absence of network APIs, UI-thread
fail-closed behavior, and hard termination of a worker stuck in native
backtracking. It also proves a 200-capture, 20,000-character sample
produces a compact bounded worker response, covers the full-text
pages operation's offsets, per-page and per-corpus bounds,
and zero-width counting, and asserts that neither
docs-hub.js nor docs-search.html contains a
bare new RegExp( — the regression guard for the UI-thread
compile that issue #69 reported. docs-search-page-test.mjs
drives the real /docs/search.html in jsdom against the real
worker on a real thread, covering plain-text default versus regex
opt-in, match-case and whole-word behavior, address round-tripping,
index-load failure, worker-absent fail-closed behavior, the regex
builder and snippet buttons, and termination of ^(a+)+$
against a 20,000-character non-matching page within the 750 ms
deadline.