Development record
The generic-account position check in the identity matchers is quadratic in line length. standsAsAccountName (internal/adapter/scanner/identity.go) lower-cases the whole line prefix before every match to test a home-literal suffix and each account-root prefix, and underAbsoluteRoot walks back to the path token's start for every non-boundary home match. A line dense in a generic login (dev, git, test, user, build, app, node: all on genericAccountNames) under a login on that list therefore costs the square of its length: the review measured 80, 160 and 320 KB at 0.92, 3.34 and 13.6 s against 31, 60 and 124 ms for a non-generic control, so one 4 MB transcript line holding a tool result costs about half an hour in the launch gate or the store-before-commit redactor. The identity matchers sat outside every count-based cost guard (iss-2609240203462704), which is why no test saw it.
The generic-account position check in the identity matchers is quadratic in line length. standsAsAccountName (internal/adapter/scanner/identity.go) lower-cases the whole line prefix before every match to test a home-literal suffix and each account-root prefix, and underAbsoluteRoot walks back to the path token's start for every non-boundary home match. A line dense in a generic login (dev, git, test, user, build, app, node: all on genericAccountNames) under a login on that list therefore costs the square of its length: the review measured 80, 160 and 320 KB at 0.92, 3.34 and 13.6 s against 31, 60 and 124 ms for a non-generic control, so one 4 MB transcript line holding a tool result costs about half an hour in the launch gate or the store-before-commit redactor. The identity matchers sat outside every count-based cost guard (iss-2609240203462704), which is why no test saw it.
Grounds
- pursued: every per-match position test reads a bounded window, so a line's identity work grows with its length; TestScanLineWorkIsLinear failing on a generic-login shape, or a wall-clock probe growing 4x per doubling, would show it wrong.
Frontmatter
| id | iss-2609251535090117 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609251535090117-the-generic-account-position-check-in.md
open on GitHub ↗ · commit history ↗
mentions1
- iss-2609240203462704The scanner's count-based cost guards measure scanAllPatterns alone, so the rest of the per-line scan is outside every cost guard. probeWork (internal/adapter/scanner/adjacency_test.go) tallies only the bytes handed to the adjacency probes and the junction probe inside scanAllPatterns; the wall-clock guards it replaced timed scanLine, which is the whole of scanText. Uncovered stages: (1) each pattern's Skip and SkipAt callbacks, run per match against the matched text and the whole line; (2) the identity matchers (matchers.findings), run over the raw line and again over the decoded copy; (3) the percent-decode pre-pass (decodedLineFindings: percentDecodeBounded, the second scanAllPatterns run over the decoded copy, mapDecodedSpan); (4) the top-level cp.Re.FindAllStringIndex pass per pattern, which is RE2-linear but untallied. A regression that makes any of these superlinear passes every guard in the package. Counting them at the scanLine level needs a seam in scanText, which builds its probes internally and constructs the identity matchers from a fixed type with no hook for a counting stand-in, so it is a production change rather than a test-only one; hence captured, not added, in the gallop lane.