AI Slop Code Risk Monitor

Automattic/harper

SlopCodeMonitor badge for Automattic/harper Adjusted legacy signal; badge bands: 25 / 50 / 75 (different from dashboard bands).

Offline, privacy-first grammar checker. Fast, open-source, Rust-powered

0.6 Low pattern signal View on GitHub

Open-Source Code Quality, Security and Technical Debt Review

This report summarizes scanner evidence for Automattic/harper, a Rust project with 17,312 GitHub stars. SynthScan examined 341,325 lines of code across 1357 source files, recording 177 pattern matches distributed across 7 syntactic categories. The legacy adjusted pattern signal is 0.6 (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.

0.6
Adjusted Signal
0.6
Raw Pattern Score
100%
Time Factor
2026-10-31
Last Push
17.3K
Stars
Rust
Language
341.3K
Lines of Code
1.4K
Files
177
Pattern Hits
2026-10-31
Scan Date
0.00
HC Hit Rate

Risk Review · Payload v1

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.

Cognitive Ownership Risk 2.4/100 · LOW

Can maintainers explain intent, assumptions, and trade-offs? 155 finding(s).

Hallucinated Dependency & Supply Chain Risk 0.3/100 · LOW

Are imports declared and package/API names verified? 14 finding(s).

Agent Over-Engineering Ratio 1.6/100 · LOW

Are wrappers, guards, and repeated structures necessary? 90 finding(s).

PR Review Velocity Impact 2.9/100 · LOW

Which changes need focused human review? 177 finding(s).

What These Metrics Mean

Adjusted Signal
Legacy pattern score normalized per 1,000 scanned lines and multiplied by the repository-level time factor. It is not an estimate of AI authorship.
Raw Pattern Score
Sum of severity- and context-weighted pattern findings before applying the time factor.
Time Factor
A coarse multiplier based on repository last-push metadata. It does not identify when individual code was written.
Pattern Hits
Count of configured pattern findings. Review each location and surrounding code before acting.
HC Hit Rate
High- and critical-severity findings per scanned file. Severity is a configured detector weight, not proof of a vulnerability or authorship.
Lines / Files
Scan coverage denominators used by the legacy normalized score.

Pattern-Signal History

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.

Severity Breakdown

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.

CRITICAL 0HIGH 0MEDIUM 13LOW 164

Directory Score Breakdown

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.

Pattern Findings

The scanner identified 177 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.

Hyper-Verbose Identifiers82 hits · 88 pts
SeverityFileLineSnippetContext
LOWpackages/obsidian-plugin/src/lint.ts867function getDiagnosticForTooltipRange(CODE
LOWpackages/obsidian-plugin/src/lint.ts974export function canApplySuggestionFromVisibleTooltip(view: EditorView, index: number): boolean {CODE
LOWpackages/obsidian-plugin/src/lint.ts980export function applySuggestionFromVisibleTooltip(view: EditorView, index: number): boolean {CODE
LOWpackages/obsidian-plugin/src/lint.ts992export function canAddWordToDictionaryFromVisibleTooltip(view: EditorView): boolean {CODE
LOWpackages/obsidian-plugin/src/lint.ts998export function addWordToDictionaryFromVisibleTooltip(view: EditorView): boolean {CODE
LOW⚡packages/obsidian-plugin/src/lint.ts1010export function canIgnoreVisibleTooltipDiagnostic(view: EditorView): boolean {CODE
LOW⚡packages/obsidian-plugin/src/lint.ts1016export function ignoreVisibleTooltipDiagnostic(view: EditorView): boolean {CODE
LOW⚡packages/obsidian-plugin/src/lint.ts1025export function canDismissFocusedLintTooltip(view: EditorView): boolean {CODE
LOW⚡packages/obsidian-plugin/src/lint.ts1030export function dismissFocusedLintTooltip(view: EditorView): boolean {CODE
LOWpackages/obsidian-plugin/src/State.test.ts24function collectStructuredRuleNames(config: StructuredLintConfig): string[] {CODE
LOWpackages/harper-editor/src/lib/editorDisplay.ts294export function createEmptyLintKindCounts(): LintKindCounts {CODE
LOWpackages/harper-editor/src/lib/Editor.svelte147async function updateLintFrameworkElements() {CODE
LOWpackages/harper-editor/src/lib/Editor.svelte274function scheduleLintFrameworkUpdate() {CODE
LOWpackages/harper-editor/src/lib/LintSidebar.svelte95function handleIgnoreBackdropClick(event: MouseEvent) {CODE
LOWpackages/wordpress-plugin/src/harper/domUtils.ts7export function extractFromHTMLCollection(collection: HTMLCollection): Element[] {CODE
LOWpackages/wordpress-plugin/src/harper/domUtils.ts32export function getNodesFromQuerySelector(element: Element, query: string) {CODE
LOWpackages/lint-framework/src/lint/googleDocsAdapter.ts21export function maybeComputeGoogleDocsLintBoxes(CODE
LOW⚡packages/lint-framework/src/lint/googleDocsAdapter.ts90export function isGoogleDocsSourceSyncing(source: SourceElement): boolean {CODE
LOW⚡packages/lint-framework/src/lint/googleDocsAdapter.ts97export function getGoogleDocsRenderTarget(source: SourceElement): HTMLElement | null {CODE
LOW⚡packages/lint-framework/src/lint/googleDocsAdapter.ts105export function getGoogleDocsRenderOffset(source: SourceElement): { x: number; y: number } | null {CODE
LOWpackages/lint-framework/src/lint/googleDocsAdapter.ts124export function applyGoogleDocsRenderHostStyle(host: HTMLElement, source: SourceElement): boolean {CODE
LOWpackages/lint-framework/src/lint/googleDocsAdapter.ts141function suggestionToReplacementText(CODE
LOWpackages/lint-framework/src/lint/domUtils.ts8export function extractFromHTMLCollection(collection: HTMLCollection): Element[] {CODE
LOWpackages/lint-framework/src/lint/domUtils.ts32export function getNodesFromQuerySelector(element: Element, query: string) {CODE
LOW…ages/lint-framework/src/lint/computeLintBoxes/index.ts105function suggestionToReplacementText(CODE
LOW…ages/lint-framework/src/lint/computeLintBoxes/index.ts217function replaceRichTextEditorValue(CODE
LOW…ages/lint-framework/src/lint/computeLintBoxes/index.ts322function replaceGenericContentEditable(CODE
LOW…ework/src/lint/computeLintBoxes/googleDocsUtilities.ts31function getGoogleDocsMeasurementContext(): CanvasRenderingContext2D | null {CODE
LOW…ework/src/lint/computeLintBoxes/googleDocsUtilities.ts41function getGoogleDocsTextSliceRect(CODE
LOW…ework/src/lint/computeLintBoxes/googleDocsUtilities.ts109function getLongestCommonSubsequenceLength(left: string, right: string): number {CODE
LOW…ework/src/lint/computeLintBoxes/googleDocsUtilities.ts235export function getGoogleDocsHighlightRects(target: HTMLElement, span: Span): DOMRect[] {CODE
LOW…ework/src/lint/computeLintBoxes/googleDocsUtilities.ts285function getGoogleDocsPositionedLeafHost(child: Node, target: HTMLElement): HTMLElement | null {CODE
LOWpackages/chrome-plugin/tests/google_docs_live.spec.ts52async function dispatchGoogleDocsBridgeReplacement(CODE
LOW…ckages/chrome-plugin/tests/google_docs_flicker.spec.ts76async function installMockGoogleDocsGeometry(page: Page, annotatedText: string) {CODE
LOW…ckages/chrome-plugin/tests/google_docs_flicker.spec.ts130async function updateMockGoogleDocsGeometry(CODE
LOW…ckages/chrome-plugin/tests/google_docs_flicker.spec.ts175async function startHighlightCountSampling(page: Page) {CODE
LOW…ckages/chrome-plugin/tests/google_docs_flicker.spec.ts197async function stopHighlightCountSampling(page: Page): Promise<number[]> {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts25export function normalizeGoogleDocsBridgeText(text: string): string {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts85export async function moveGoogleDocsCursorToEnd(page: Page) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts94async function getGoogleDocsRenderedState(page: Page): Promise<GoogleDocsRenderedState> {CODE
LOW⚡packages/chrome-plugin/tests/googleDocsTestUtils.ts118export async function getGoogleDocsAnnotatedText(page: Page): Promise<string> {CODE
LOW⚡packages/chrome-plugin/tests/googleDocsTestUtils.ts128export async function getGoogleDocsNormalizedBridgeText(page: Page): Promise<string> {CODE
LOW⚡packages/chrome-plugin/tests/googleDocsTestUtils.ts132export async function getGoogleDocsHighlightCount(page: Page): Promise<number> {CODE
LOW⚡packages/chrome-plugin/tests/googleDocsTestUtils.ts137export async function getGoogleDocsVisualLineCount(page: Page): Promise<number> {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts160async function clearGoogleDocsDocumentCompletely(page: Page) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts206async function openGoogleDocsInsertTablePicker(page: Page) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts272async function ensurePlainGoogleDocsInsertionMode(page: Page) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts312export async function replaceGoogleDocsDocumentText(page: Page, text: string) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts370export async function selectGoogleDocsFromTextToEnd(page: Page, text: string) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts390export async function hasGoogleDocsRectWithFontContainingText(CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts405export async function applyItalicToGoogleDocsText(page: Page, text: string, occurrence = 0) {CODE
LOWpackages/chrome-plugin/tests/googleDocsTestUtils.ts416export async function applyBulletedListToGoogleDocsText(page: Page, text: string) {CODE
LOWpackages/chrome-plugin/tests/google_docs.spec.ts406async function installMockGoogleDocsGeometry(CODE
LOWpackages/chrome-plugin/tests/testUtils.ts116export async function waitForHighlightsToSettle(CODE
LOWpackages/chrome-plugin/tests/testUtils.ts161export async function waitForHarperHighlightCenter(CODE
LOWpackages/chrome-plugin/tests/testUtils.ts195export async function assertLocatorsResolveEqually(page: Page, a: Locator, b: Locator) {CODE
LOWpackages/chrome-plugin/tests/testUtils.ts228export async function openHarperPopupFromEditorPointerDown(CODE
LOWpackages/chrome-plugin/tests/testUtils.ts383export async function testCanBlockRuleSuggestion(CODE
LOWpackages/chrome-plugin/tests/testUtils.ts429export async function testMultipleSuggestionsAndUndo(CODE
LOWpackages/chrome-plugin/tests/testUtils.ts483export async function assertHarperHighlightBoxes(CODE
22 more matches not shown…
Over-Commented Block63 hits · 63 pts
SeverityFileLineSnippetContext
LOWharper-ls/src/main.rs21COMMENT
LOW…t-commit/tests/test_sources/complex_verbose_commit.txt1This is the the subjectCOMMENT
LOWpackages/obsidian-plugin/src/lint.ts41 /// this diagnostic applies to.COMMENT
LOWpackages/obsidian-plugin/src/lint.ts81 delay?: number;COMMENT
LOWpackages/harper.js/src/Linter.test.ts781}COMMENT
LOWharper-core/expr_linter_skeleton_commented.rs41 fn match_to_lint_with_context(COMMENT
LOWharper-core/expr_linter_skeleton_commented.rs61 // EDIT using `.span()` from `TokenStringExt`.COMMENT
LOWharper-core/tests/pos_tags.rs1//! This test creates snapshots of the part-of-speech (POS) tags assigned by theCOMMENT
LOWharper-core/tests/pos_tags.rs21//! > I told her how I had stopped off in Chicago for a day on my way East .COMMENT
LOWharper-core/tests/pos_tags.rs41//! - The `Pl` suffix means plural, and `Sg` means singular.COMMENT
LOWharper-core/tests/pos_tags.rs61//! - Dialects are denoted by `Am`, `Br`, `Ca`, or `Au` for individualCOMMENT
LOWharper-core/src/dict_word_metadata.rs41 /// If no dialects are defined, it can be assumed that the word isCOMMENT
LOWharper-core/src/dict_word_metadata.rs761 /// Does the metadata for this word cover an all-lowercase variant? (e.g., "hello")COMMENT
LOWharper-core/src/dict_word_metadata.rs781 self.orth_info.contains(OrthFlags::TITLECASE)COMMENT
LOWharper-core/src/dict_word_metadata.rs801 ///COMMENT
LOWharper-core/src/dict_word_metadata.rs881//COMMENT
LOWharper-core/src/dict_word_metadata.rs1141 /// Tries to get a dialect from its abbreviation. Returns `None` if the abbreviation is notCOMMENT
LOWharper-core/src/dict_word_metadata.rs1221 /// Checks if the provided dialect is enabled.COMMENT
LOWharper-core/src/dict_word_metadata.rs1241 ///COMMENT
LOWharper-core/src/punctuation.rs21 /// `.`COMMENT
LOWharper-core/src/span.rs1use std::{fmt::Display, marker::PhantomData, ops::Range};COMMENT
LOWharper-core/src/token_string_ext.rs121 /// assert_eq!(tokens.get_rel(0).unwrap().get_str(&source), "The");COMMENT
LOWharper-core/src/token_string_ext.rs141 }COMMENT
LOWharper-core/src/linting/lint_kind.rs41 /// When your brain doesn't know the right spelling.COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs141 RiseTheRanks::default(),COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs161 // }COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs181 // #[test]COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs201 // assert_suggestion_result(COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs221 // RiseTheRanks::default(),COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs241 // );COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs261COMMENT
LOWharper-core/src/linting/rise_the_ranks.rs281 // fn fix_risen_in_the_ranks() {COMMENT
LOWharper-core/src/linting/there_is_agreement.rs721 &[COMMENT
LOWharper-core/src/linting/mod.rs401/// application cycles or deep multi-step fixes are involved.COMMENT
LOWharper-core/src/linting/expr_linter.rs41 type Unit: DocumentIterator;COMMENT
LOWharper-core/src/linting/adjective_of_a.rs81COMMENT
LOWharper-core/src/linting/nobel_noble.rs241 NobelNoble::default(),COMMENT
LOWharper-core/src/linting/lint_group/mod.rs381};COMMENT
LOWharper-core/src/spell/rune/attribute_list.rs41 .map_err(Error::from)COMMENT
LOWharper-core/src/spell/word_map/mod.rs21/// Type used to store the canonical entries in the word map.COMMENT
LOWharper-core/src/expr/fixed_phrase.rs1use crate::parsers::PlainEnglish;COMMENT
LOWharper-core/src/expr/expr_map.rs1use crate::LSend;COMMENT
LOWharper-core/src/expr/mod.rs1//! An `Expr` is a declarative way to express whether a certain set of tokens fulfill a criteria.COMMENT
LOWharper-core/src/expr/filter.rs1use crate::{Span, Token};COMMENT
LOWharper-cli/src/main.rs61 Lint {COMMENT
LOWharper-cli/src/main.rs81 #[arg(short, long, default_value = "us")]COMMENT
LOWharper-cli/src/main.rs101 },COMMENT
LOWharper-cli/src/main.rs181 #[arg(short, long)]COMMENT
LOW…src-tauri/src/highlighter/render_state/popup_layout.rs1//! Places the suggestion list card and the lint flyout card on screen.COMMENT
LOW…src-tauri/src/highlighter/render_state/popup_layout.rs21/// The time constant, in seconds, of the sidebar's movement toward its target.COMMENT
LOW…src-tauri/src/highlighter/render_state/popup_layout.rs41impl PopupLayout {COMMENT
LOWharper-desktop/src/routes/+layout.ts1// Tauri doesn't have a Node.js server to do proper SSRCOMMENT
LOW…r-comments/tests/language_support_sources/issue_229.cs1// COMMENT
LOW…r-comments/tests/language_support_sources/issue_229.js1// COMMENT
LOW…omments/tests/language_support_sources/merged_lines.ts1// This is a test to make sure we don't split up paragraphs on newlines.COMMENT
LOW…er-comments/tests/language_support_sources/issue_229.c1// COMMENT
LOWharper-comments/tests/language_support_sources/clean.rs1/// This is an example Rust file that should produce no Harper lints.COMMENT
LOW…nts/tests/language_support_sources/ignore_shebang_3.sh1#!/usr/bin/python3COMMENT
LOW…nts/tests/language_support_sources/ignore_shebang_2.sh1#! /usr/bin/env shCOMMENT
LOW…nts/tests/language_support_sources/ignore_shebang_1.sh1#!/bin/shCOMMENT
3 more matches not shown…
Decorative Section Separators8 hits · 24 pts
SeverityFileLineSnippetContext
MEDIUMharper-core/src/linting/were_where.rs132 // ── where → were: unambiguous pronouns ──────────────────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs161 // ── where → were: "you where" with a following verb ─────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs181 // ── were → where: verb + were + pronoun/determiner ──────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs219 // ── where → were: more they/we variants ─────────────────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs258 // ── where → were: more "you where" variants ──────────────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs289 // ── were → where: more verbs and pronouns ────────────────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs336 // ── no false positives ───────────────────────────────────────────────────COMMENT
MEDIUMharper-core/src/linting/were_where.rs415 // ── known limitations (documented but not yet handled) ───────────────────COMMENT
Fake / Example Data14 hits · 18 pts
SeverityFileLineSnippetContext
LOWpackages/web/src/routes/languagedetection/+page.svelte34 name: 'Chinese Lorem Ipsum',CODE
LOWpackages/web/src/routes/titlecase/+page.svelte37 attr: 'placeholder',CODE
LOWpackages/chrome-plugin/tests/google_docs_live.spec.ts345 const repeatedSentence = 'Lorem ipsum an dolor sit amet.';CODE
LOWpackages/chrome-plugin/tests/google_docs_live.spec.ts345 const repeatedSentence = 'Lorem ipsum an dolor sit amet.';CODE
LOW⚡packages/chrome-plugin/tests/google_docs_live.spec.ts379 .toBe(`Lorem ipsum an dolor sit amet. ${insertedPrefix}Lorem ipsum a dolor sit amet.`);CODE
LOW⚡packages/chrome-plugin/tests/google_docs_live.spec.ts379 .toBe(`Lorem ipsum an dolor sit amet. ${insertedPrefix}Lorem ipsum a dolor sit amet.`);CODE
LOW⚡packages/chrome-plugin/tests/google_docs_live.spec.ts382 .toBe(`Lorem ipsum an dolor sit amet. ${insertedPrefix}Lorem ipsum a dolor sit amet.`);CODE
LOW⚡packages/chrome-plugin/tests/google_docs_live.spec.ts382 .toBe(`Lorem ipsum an dolor sit amet. ${insertedPrefix}Lorem ipsum a dolor sit amet.`);CODE
LOWpackages/components/src/lib/SearchField.svelte11export let placeholder: HTMLInputAttributes['placeholder'] = undefined;CODE
LOWpackages/components/src/lib/Input.svelte29export let placeholder: HTMLInputAttributes['placeholder'] = undefined;CODE
LOW⚡packages/harper.js/src/Linter.test.ts167 test(`${linterName} detects lorem ipsum paragraph as not english`, async () => {CODE
LOW⚡packages/harper.js/src/Linter.test.ts171 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna CODE
LOW⚡packages/harper.js/src/Linter.test.ts171 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna CODE
LOWharper-core/tests/test_sources/chinese_lorem_ipsum.md1The following text was generated using [a Chinese lorem ipsum generator](https://pinkylam.me/generator/chinese-lorem-ipsCODE
Slop Phrases5 hits · 8 pts
SeverityFileLineSnippetContext
LOWharper-core/src/linting/open_the_light.rs186 "Don't forget to open the lights when you enter the room.",CODE
LOWharper-core/src/linting/open_the_light.rs188 "Don't forget to turn on the lights when you enter the room.",CODE
MEDIUMharper-core/src/linting/initialisms.rs235 "Let me know if you need any debug logs.",CODE
MEDIUM…src/linting/phrase_set_corrections/one_to_one_tests.rs841 "Here is how you can extract the list of coords from any geometry:",CODE
MEDIUM…src/linting/phrase_set_corrections/one_to_one_tests.rs843 "Here is how you can extract the list of coordinates from any geometry:",CODE
AI Slop Vocabulary4 hits · 7 pts
SeverityFileLineSnippetContext
MEDIUMpackages/web/src/routes/wpdemo/+page.server.ts5const playgroundUrl = `https://playground.wordpress.net/?mode=seamless#${base64Blueprint}`;CODE
MEDIUMpackages/harper.js/src/SlimLinter.test.ts5// We test the 'slim' binary separately from the main one because we utilize memoization.COMMENT
LOWharper-core/src/linting/call_them.rs144 r#"Can we just call it as abstract latte art."#,CODE
LOWharper-core/src/linting/call_them.rs146 r#"Can we just call it abstract latte art."#,CODE
Overly Generic Function Names1 hit · 1 pts
SeverityFileLineSnippetContext
LOWpackages/chrome-plugin/src/background/index.ts222function handleRequest(message: Request, sender?: chrome.runtime.MessageSender): Promise<Response> {CODE