Fast, accurate & comprehensive text measurement & layout
This report summarizes scanner evidence for chenglou/pretext, a TypeScript project with 50,975 GitHub stars. SynthScan examined 84,730 lines of code across 169 source files, recording 186 pattern matches distributed across 7 syntactic categories. The legacy adjusted pattern signal is 2.4 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 185 finding(s).
Are imports declared and package/API names verified? 1 finding(s).
Are wrappers, guards, and repeated structures necessary? 33 finding(s).
Which changes need focused human review? 186 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 186 distinct pattern matches across 7 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | harness/invariants.ts | 1 | // What an app relies on in the line APIs that no recording shows, checked offline. `bun test harness` runs it in one | COMMENT |
| LOW | harness/invariants.ts | 21 | // The inputs are seeded draws from harness/cases (a failure names its case, at its width, half and 1.5 times it, 1 and | COMMENT |
| LOW | harness/invariants.ts | 41 | // - held handles, and their structuredClone() copies, lay out as before after the same texts are prepared with letter | COMMENT |
| LOW | harness/icu.test.ts | 1 | // The library's port of ICU's rule-based break iterator and its packed tables (src/line-breaks.ts, src/graphemes.ts) | COMMENT |
| LOW | harness/cli.ts | 1 | // bun harness <command> [--browser=chrome|firefox|webkit-host|safari|ios|all] [--cases=<file.ndjson>] | COMMENT |
| LOW | harness/cli.ts | 21 | // safari records webkit-host and installed Safari's sample, and prints whether they agree | COMMENT |
| LOW | harness/offline-equal.ts | 1 | // `bun harness equal <ref> --offline`: whether two builds' src/ give the same results on the invariants' stand-in Canva | COMMENT |
| LOW | harness/browsers.ts | 1 | // The browsers a job runs in, and the environment key a recording is kept under. | COMMENT |
| LOW | harness/browsers.ts | 141 | } | COMMENT |
| LOW | harness/observe.ts | 1 | // How `record` reads the browser's own layout of a case. The DOM part runs in the page; the line functions below it als | COMMENT |
| LOW | harness/types.ts | 1 | // The shapes the harness passes between its files: a case, what the browser recorded for it, and what a library build | COMMENT |
| LOW | harness/types.ts | 21 | // - `hyphenCopies`: Chrome reports a soft hyphen's box for the code point next to it too, which the recorder leaves out | COMMENT |
| LOW | harness/predict.ts | 1 | // The library used the way an app uses it, in the page: one Canvas font string per run style, maxWidth = the case's | COMMENT |
| LOW | harness/watchdog.ts | 1 | // Kills its process once it holds more than 1 GB, 2 GB for bun test itself, once the process that started it is gone, o | COMMENT |
| LOW | harness/run.ts | 81 | // Pinned Chrome and Firefox each open a window on the user's screen, so there the page is dark and its title says the | COMMENT |
| LOW | harness/store.ts | 1 | // The files the harness keeps in git, all plain text with one entry per line, sorted, and nothing that changes from one | COMMENT |
| LOW | harness/bench/report.ts | 1 | // What the bench prints: the builds, then per browser, row and family or operation, base's and the candidate's cost (pe | COMMENT |
| LOW | harness/bench/texts.ts | 1 | // The bench's texts. Chat-like messages are read in order from each family's own part of the corpora, so no message, | COMMENT |
| LOW | harness/webkit-host/main.swift | 1 | // webkit-host: one background WKWebView on the system WebKit.framework, the engine installed Safari runs, so the harnes | COMMENT |
| LOW | harness/sets/make.ts | 1 | // Makes the case files in harness/cases. Sets with a given width are written directly; the catalog, the engine facts an | COMMENT |
| LOW | harness/sets/sample.ts | 1 | // The real-usage sample: paragraphs drawn in proportion to how often apps lay them out, from weights.json, with a | COMMENT |
| LOW | harness/sets/widths.ts | 1 | // The widths where each browser's lines change, and the rules that cut a generated set down to them. | COMMENT |
| LOW | harness/sets/rich.ts | 1 | // Rich-inline templates: paragraphs of spans, which the adapter lays out with rich-inline (one item per run, a chip as | COMMENT |
| LOW | harness/sets/rich.ts | 21 | // to a ZWSP in another item, and a soft hyphen after a space in an item that continues a run from an earlier item, | COMMENT |
| LOW | harness/sets/rich.ts | 41 | // follows, beside the same message without keep-all; a mention chip inside a Korean word; and Japanese whose bold run | COMMENT |
| LOW | harness/sets/rich.ts | 61 | // moves to the next line with them; | COMMENT |
| LOW | harness/sets/rich.ts | 81 | // line it lays out again between graphemes (itemEndHalts in src/line-break.ts). Each is three or four characters, so | COMMENT |
| LOW | harness/sets/rich.ts | 101 | // paragraph of one item, which the adapter writes with rich-inline for the empty span and whose lines are its | COMMENT |
| LOW | harness/sets/rich.ts | 121 | // the space fits its line and the space doesn't, all three browsers start the next line with the ZWSP, which holds | COMMENT |
| LOW | harness/sets/rich.ts | 141 | // the same item without the ZWNJ, whose lines all carry it. In Chrome both templates also have other lines than | COMMENT |
| LOW | harness/sets/rich.ts | 161 | // start edge of its own, a segment a line can end after (getOpeningFit); a padded item of a space, a tab and a | COMMENT |
| LOW | harness/sets/rich.ts | 181 | // moves it down (ENGINE_FOLLOWUPS.md, White space and controls): in 16px Courier New, whose hyphen is as wide as its | COMMENT |
| LOW | harness/sets/facts.ts | 1 | // main's engine facts as browser cases. When this set was made, src/layout.test.ts pinned 36 rules main learned about | COMMENT |
| LOW | harness/sets/catalog.ts | 1 | // The behaviour catalog's templates, before widths.ts cuts them. Eleven sources: | COMMENT |
| LOW | harness/sets/data/LineBreak-17.0.0.txt | 1 | # LineBreak-17.0.0.txt | COMMENT |
| LOW | harness/sets/data/LineBreak-17.0.0.txt | 21 | # "AI", "AK", "AL", "AP", "AS", "B2", "BA", "BB", "CB", "CJ", | COMMENT |
| LOW | harness/sets/data/LineBreak-17.0.0.txt | 41 | # | COMMENT |
| LOW | scripts/generate-engine-break-data.ts | 1 | // Generates src/generated/engine-break-data.ts, the tables behind Chrome's and Safari's | COMMENT |
| LOW | scripts/generate-engine-break-data.ts | 21 | // - char.brk: the brkitr/char.brk entry of Chrome 153.0.8010.53's icudtl.dat, the same bytes | COMMENT |
| LOW | scripts/generate-engine-break-data.ts | 41 | // firefox-156/, from Firefox 155.0.1's source tree. Firefox 156.0's source tree has the same two | COMMENT |
| LOW | scripts/generate-engine-break-data.ts | 541 | // shape_result.cc:977-990; UnicodeProperties.h:350-355): Arabic, Mongolian, Syriac, Mandaic, | COMMENT |
| LOW | scripts/generate-engine-break-data.ts | 821 | const nextSource = `// Generated by scripts/generate-engine-break-data.ts from scripts/engine-data/. | COMMENT |
| LOW | scripts/generate-webkit-generic-families.ts | 1 | // Generates src/generated/webkit-generic-families.ts, what Safari's page resolves the generic | COMMENT |
| LOW | scripts/generate-webkit-generic-families.ts | 21 | // WebKit's rules on Core Text's answer: a name with a leading '.' isn't used and the settings' | COMMENT |
| LOW | pages/demos/wrap-geometry.ts | 121 | // Given one allowed horizontal interval and a set of blocked intervals, | COMMENT |
| LOW | src/prepare.ts | 101 | COMMENT | |
| LOW | src/prepare.ts | 241 | // shaping run the cluster is in and spaces only its spaces (ComputeSpacing, | COMMENT |
| LOW | src/prepare.ts | 281 | COMMENT | |
| LOW | src/prepare.ts | 361 | // Whether text segments take emergency breaks between graphemes: an atomic rich item, | COMMENT |
| LOW | src/prepare.ts | 381 | // walkers, layout()'s count and rich-inline layout read where the scan gives no break. | COMMENT |
| LOW | src/prepare.ts | 641 | } | COMMENT |
| LOW | src/prepare.ts | 681 | // Blink (Chrome 153) hangs a run of U+3000 that ends a line, as it hangs spaces: ShapingLineBreaker | COMMENT |
| LOW | src/line-break.ts | 21 | const BREAK_AFTER_KINDS = 1 << SPACE | 1 << ZERO_WIDTH_BREAK | 1 << SOFT_HYPHEN | 1 << PRESERVED_SPACE | 1 << TAB | COMMENT |
| LOW | src/line-break.ts | 41 | // Per segment, width a line that starts with it adds back, which its width leaves out | COMMENT |
| LOW | src/line-break.ts | 61 | COMMENT | |
| LOW | src/line-break.ts | 81 | // src/rich-inline.ts); | COMMENT |
| LOW | src/line-break.ts | 141 | // What a line's start leaves out, by a segment's flags: a collapsible space, a soft hyphen, and a | COMMENT |
| LOW | src/line-break.ts | 421 | // between graphemes breaks before the white space instead. But Gecko breaks only after a run of | COMMENT |
| LOW | src/line-break.ts | 501 | // so in a rich-inline paragraph the run of preserved spaces that hang goes on, with what | COMMENT |
| LOW | src/line-break.ts | 541 | let lineEndGraphemeIndex = lineStartGraphemeIndex | COMMENT |
| 81 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | harness/sets/catalog.ts | 293 | export function letterSpacedLigatureTemplates(): Template[] { | CODE |
| LOW | scripts/build-demo-site.ts | 78 | function rewriteDemoLinksForStaticRoot(html: string, targetRelativePath: string): string { | CODE |
| LOW | pages/demos/dynamic-layout.ts | 863 | requestAnimationFrame(function renderAndMaybeScheduleAnotherRender(now) { | CODE |
| LOW | pages/demos/markdown-chat.model.ts | 219 | export function createPreparedChatMessages(): PreparedChatMessage[] { | CODE |
| LOW | pages/demos/markdown-chat.model.ts | 496 | function buildPreparedInlineBlocks( | CODE |
| LOW | pages/demos/markdown-chat.model.ts | 845 | function stripSingleTrailingNewline(text: string): string { | CODE |
| LOW | pages/demos/wrap-geometry.ts | 73 | export function getPolygonIntervalForBand( | CODE |
| LOW | pages/demos/editorial-engine.ts | 558 | function isTextSelectionInteractionActive(): boolean { | CODE |
| LOW | pages/demos/editorial-engine.ts | 565 | scheduledRaf = requestAnimationFrame(function renderAndMaybeScheduleEditorialFrame(now) { | CODE |
| LOW | pages/demos/justification-comparison.ts | 74 | requestAnimationFrame(function syncCssOverlayAfterLayout() { | CODE |
| LOW | pages/demos/justification-comparison.model.ts | 238 | function buildMeasuredLineFromLayoutResult( | CODE |
| LOW | pages/demos/justification-comparison.model.ts | 353 | function getLineStatsFromBreakCandidates( | CODE |
| LOW | pages/demos/justification-comparison.model.ts | 405 | function buildMeasuredLineFromCandidateRange( | CODE |
| LOW | pages/demos/masonry/index.ts | 103 | function getOrCreateCardNode(cardIndex: number): HTMLDivElement { | CODE |
| LOW | pages/demos/masonry/index.ts | 125 | scheduledRaf = requestAnimationFrame(function renderAndMaybeScheduleAnotherRender() { | CODE |
| LOW | src/prepare.ts | 57 | function countRenderedSpacingGraphemes(text: string, kind: SegmentKindCode, graphemeTable: GraphemeTable): number { | CODE |
| LOW | src/line-break.ts | 253 | export function normalizePreparedLineStart( | CODE |
| LOW | src/line-break.ts | 1092 | function stepPreparedSimpleLineGeometry( | CODE |
| LOW | src/line-break.ts | 1169 | export function stepPreparedLineGeometryFromStart( | CODE |
| LOW | src/gecko-line-breaks.ts | 75 | function isClusterExtenderExcludingJoiners(cp: number): boolean { | CODE |
| LOW | src/gecko-line-breaks.ts | 81 | function isEastAsianWidthFHWExcludingEmoji(widths: ClassTable, cp: number): boolean { | CODE |
| LOW | src/gecko-line-breaks.ts | 122 | export function isSpaceCombiningSequenceTail(text: string, from: number): boolean { | CODE |
| LOW | src/gecko-line-breaks.ts | 316 | function hasCompressedLeadingWhitespace(source: string, skipped: Uint8Array, is8bit: boolean, preserveWhiteSpace: boolea | CODE |
| LOW | src/measurement.ts | 1274 | function countLigaturesOnFirstLetter(seg: string, ends: Int32Array, advances: number[], measurement: FontMeasurement, em | CODE |
| LOW | src/line-breaks.ts | 872 | export function getWebKitBreakBetweenItems(previous: string, next: string, keepAll: boolean, language: string | null): b | CODE |
| LOW | src/layout.test.ts | 198 | function reconstructFromLineBoundaries( | CODE |
| LOW | src/analysis.ts | 125 | export function removeSkippableSegmentBreaks(text: string, profile: AnalysisProfile, language: string | null = null): st | CODE |
| LOW | src/analysis.ts | 167 | function getTrailingCollapsibleStart(text: string, from: number, profile: AnalysisProfile): number { | CODE |
| LOW | src/analysis.ts | 195 | function normalizeWhitespacePreWrap(text: string): string { | CODE |
| LOW | src/analysis.ts | 583 | function removeItemsSkippableSegmentBreaks(text: string, starts: number[], profile: AnalysisProfile, language: string | | CODE |
| LOW | src/rich-inline.ts | 182 | function getInternalPreparedRichInline(prepared: PreparedRichInline): InternalPreparedRichInline { | CODE |
| LOW | src/rich-inline.ts | 1110 | export function layoutNextRichInlineLineRange( | CODE |
| LOW | src/rich-inline.ts | 1192 | export function materializeRichInlineLineRange( | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | harness/browsers.ts | 564 | const marker = JSON.stringify(`about:blank#pretext-harness-${jobId}`) | CODE |
| MEDIUM | harness/webkit-host/build.sh | 25 | # The harness runs only a host built from main.swift as it is (harness/browsers.ts). | COMMENT |
| MEDIUM | harness/sets/make.ts | 9 | // bun harness/sets/make.ts cut <set> # the cut cases, into harness/cases/<set>.ndjson, with | COMMENT |
| MEDIUM | harness/sets/make.ts | 12 | // # template of the set (harness/README.md, How cases grow) | COMMENT |
| MEDIUM | harness/sets/facts.ts | 6 | // those of main before #340 (harness/README.md, Adding a case), and the number names its family, so both stay as the | COMMENT |
| MEDIUM⚡ | pages/demos/masonry/shower-thoughts.json | 1 | ["Men's public restrooms are laid out all wrong. It should be urinal, stall, urinal, stall, urinal instead of urinal, ur | CODE |
| MEDIUM⚡ | pages/demos/masonry/shower-thoughts.json | 1 | ["Men's public restrooms are laid out all wrong. It should be urinal, stall, urinal, stall, urinal instead of urinal, ur | CODE |
| LOW⚡ | pages/demos/masonry/shower-thoughts.json | 1 | ["Men's public restrooms are laid out all wrong. It should be urinal, stall, urinal, stall, urinal instead of urinal, ur | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH⚡ | pages/demos/masonry/shower-thoughts.json | 1 | ["Men's public restrooms are laid out all wrong. It should be urinal, stall, urinal, stall, urinal instead of urinal, ur | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | harness/sets/data/LineBreak-17.0.0.txt | 1927 | B77D..B797 ; H3 # Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | pages/demos/masonry/shower-thoughts.json | 1 | ["Men's public restrooms are laid out all wrong. It should be urinal, stall, urinal, stall, urinal instead of urinal, ur | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | harness/sets/sample.ts | 457 | () => `192.168.${rng.int(255)}.${rng.int(255)}`, () => `+1 (415) 555-${n(4)}`, () => 'user@example.com', () => 'N/A' | CODE |