Skip to content

TeaWithLucas/Glyphguard

Repository files navigation

GlyphGuard

GlyphGuard provides an interface for inspecting documents, pasting text, inspecting suspicious Unicode and AI artefacts in real time, tuning what gets flagged, and exporting the full report when needed. It works locally, so you can review what was found and decide what matters without sending your text anywhere.

Main GlyphGuard window with a loaded file and collapsed detection panel

What it does

GlyphGuard is a small local inspection tool for text and documents. It shows suspicious Unicode, hidden characters, unusual punctuation, private-use markers, leaked citation plumbing, raw tool metadata and repeated generated-writing patterns. It does not prove authorship and it does not send the text to an external detector. It gives you a structured set of things to check.

Why use it

Some problems are easy to see, such as a paragraph that is full of generic generated prose or a copied tool marker left in the final text. Other problems are much easier to miss: zero-width characters inside words, non-breaking spaces that break search, direction controls that change display order, private-use citation markers, malformed metadata, or odd Unicode that causes problems in diffs, moderation tools, filenames and parsers.

GlyphGuard is useful for:

  • checking pasted text before publishing it;
  • reviewing documents, wiki edits, Markdown, logs or support text without using an online detector;
  • finding hidden or unusual Unicode that is not obvious in normal editors;
  • spotting leaked AI/tool artefacts, citation placeholders and raw metadata;
  • comparing weak writing-style hints with stronger evidence such as copied tool output;
  • exporting a simple report when you need to show what was found and where.

It is a small tool I made for practical review work, not a polished detection service. The point is to make the suspicious bits visible so you can decide what matters.

GlyphGuard does not prove that a passage was written by AI. Its heuristic rules highlight review signals: unusual glyphs, leaked tool markers, copied citation plumbing, generic generated-prose patterns and other artefacts worth checking.

Features

This is the high-level overview. Later sections explain how to interpret the results, run the scorer, add samples and update rules.

GlyphGuard is built with Tkinter and standard Python libraries. Report generation uses matplotlib. It runs locally on Windows, macOS and Linux.

Unicode and glyph inspection

Paste text or load a supported file and GlyphGuard lists non-ASCII characters with their code point, Unicode name, category, concern level, count and positions. It covers invisible controls, non-breaking spaces, bracket alternatives, dash and quote variants, fullwidth forms, private-use markers and other derived Unicode oddities.

This is useful for AI review because some AI tools leak private-use markers or odd citation characters. It is also useful outside AI detection because hidden characters can be used for prompt injection, confusing diffs, broken search, bad copy/paste, parser failures, misleading identifiers, or text that displays differently from how it is stored.

Character detection panel showing suspicious glyphs

Useful checks include:

  • zero-width spaces, joiners and non-joiners hidden inside ordinary words;
  • direction controls and invisible formatting characters;
  • curly quotes, long dashes, fullwidth letters or strange brackets before pasting into strict systems;
  • private-use characters left behind by citation or rendering tools;
  • replacement suggestions for making a clean copy of the text.

The UI also uses semantic badges. Character rows show category badges, and holistic rules are tagged by evidence type such as LLM Style, Structure, Evidence Gap, Citation Leak, Metadata Leak, API Leak, Punctuation Pattern and vendor/model tags where the rule is specific enough. Dense badge rows collapse behind a +N expander when needed.

AI artefact detection

GlyphGuard uses a curated set of regular expressions to catch vocabulary, phrasing, structure, citation leaks, tool artefacts and API metadata that are often worth reviewing in generated or copy-pasted AI output.

A compact example of the sort of thing it is meant to catch:

The source says this is supported by [12†L44-L51] and turn0file3.

The exact rendered marker changes by tool, but this is the shape of the problem: citation plumbing that should have been converted into normal prose, a normal footnote, or a normal link before the text was published.

Detection panel showing phrase and artefact matches

Rule families cover OpenAI browser and Deep Research citations, private-use citation markers, source IDs, Harmony tokens, File Search metadata, Claude citation metadata, Gemini grounding metadata and Perplexity/Sonar search metadata.

Holistic writing-pattern rules

Holistic rules are broader writing-pattern checks. They look for repeated generated-prose habits, formulaic structures, vague attribution, suspiciously generic framing, evidence gaps and punctuation patterns.

These rules are scored because a single weak phrase is not very meaningful. Repeated matches across a short text are more interesting than one isolated match in a long document. The score is a review priority, not a probability and not an authorship verdict.

Each holistic rule can include static examples, live matches from the scanned text, source references, semantic tags, positions, severity and a suggested review action. Examples explain the rule. Matches are the actual snippets found in your text.

Document and pasted-text workflow

You can paste text directly into the input pane or import a file. Supported input covers plain text, Markdown, logs, CSV/TSV, JSON, YAML, TOML, INI, source-code files, HTML/XML/SVG, DOCX, ODT, RTF and text-based PDFs.

The application scans pasted text and loaded document content as you work, updating counts and flags immediately without server calls. You can collapse noisy panels, click position pills to jump back to the source text, hover source badges for provenance, and adjust character detection and replacement behaviour in preferences.

For plain text sources, GlyphGuard can save cleaned text back to the original file or save a clean copy. For document formats such as DOCX, ODT, RTF and PDF, extraction is read-only; use Save as or Save clean copy.

Reports and exports

GlyphGuard can export detailed PDF and plain-text reports. Reports include summary scores, glyph detections, holistic matches, examples, sources, positions and charts.

First page of an exported GlyphGuard report

The headless scorer can also scan files and folders without opening the Tkinter UI.

Sample corpus and scoring

The samples/ folder currently contains samples/wikipedia_signs_ai_writing/, a bundled set of 198 text samples extracted from Wikipedia's "Signs of AI writing" page. The set contains 175 quote examples and 23 preformatted examples.

The corpus is for detector tuning, regression checks and quick scoring experiments. It is not a claim that every sample is AI-generated.

Quick start

Requirements

  • Python 3.8 or newer.
  • matplotlib for PDF report generation.
  • Tkinter for the desktop UI. It is bundled with many Python installs. On some Linux distributions you may need to install it separately, for example with python3-tk.
  • pytest if you want to run tests.

Install

From a fresh clone:

git clone <repo-url>
cd glyphguard
python -m pip install matplotlib

For development:

python -m pip install matplotlib pytest

There is no packaging wrapper at the moment. Run the files from the repository root.

Run the app

python glyphguard.py

The app opens a resizable desktop window. Paste text into the input pane or use Import to load a document. If the window does not open on Linux, check that Tkinter is installed.

Run the sample scorer

Default run, using the bundled sample corpus when present:

python score_samples.py

Score the bundled corpus explicitly:

python score_samples.py samples/wikipedia_signs_ai_writing --out results

Score a specific file or folder:

python score_samples.py README.md docs --out results

Choose extensions when scoring a mixed folder:

python score_samples.py docs --extensions .txt,.md,.html,.docx,.pdf --out results

Run a quick smoke test:

python score_samples.py samples/wikipedia_signs_ai_writing --limit 10 --out /tmp/glyphguard_score_smoke

The older --samples option is still accepted as an alias for one input file or folder, but positional paths are the normal form now.

Typical workflow

  1. Launch GlyphGuard.
  2. Paste text, or click Import and choose a file.
  3. Review the score cards and detection lists.
  4. Expand rows to see positions, examples, tags and sources.
  5. Use the clean-text preview to check replacements.
  6. Export a PDF or text report if you need to share the findings.

Understanding results

GlyphGuard results should be read as review prompts. Strong artefacts such as leaked private-use markers or raw citation metadata are different from weak style hints such as a generic transition. The useful question is not "did the tool accuse this text?" but "what exactly did it find, how strong is that signal, and does the surrounding text support that concern?"

A good review usually works in this order:

  1. Check very-high and high severity artefacts first.
  2. Check whether the match is a hard leak, a hidden character, or only a writing-style hint.
  3. Use positions and snippets to inspect the original context.
  4. Treat repeated weak signals as more interesting than isolated weak signals.
  5. Keep false positives and false negatives in mind.

Severity levels

GlyphGuard uses practical concern levels:

  • None - visible but not normally a problem, or kept as a no-concern review signal.
  • Low - weak hint or low-risk formatting oddity.
  • Medium - worth checking, especially if repeated.
  • High - likely to matter, such as malformed markup, suspicious metadata or stronger style patterns.
  • Very high - serious glyph concern or mapped critical artefact.
  • Critical - internal holistic-rule severity for hard tool leaks, prompt leaks, raw metadata or placeholder artefacts. In the UI this maps into the very-high concern bucket.

Rule matches

Rules have static examples in the code and reports. Those examples are not evidence from the current scan; they are there to show what the rule is looking for.

Matches are different. A match is text found in the document you scanned, with positions where possible. When reviewing a rule row, use examples to understand the rule and matches to inspect the actual evidence.

Glyph matches

Glyph rows show the character code point, Unicode name, category, concern level, count, positions and possible replacement. Unknown non-ASCII characters can still be derived from Python's Unicode metadata and shown for review even when they are not in the curated list.

Replacement suggestions are deliberately conservative. Some characters should be removed, some should be replaced with plain ASCII, and some should be kept because they are normal in the right context. The clean-text preview is there so you can check the result before saving anything.

Tags, categories and sources

Holistic rules use semantic tags such as LLM Style, Structure, Evidence Gap, Citation Leak, Metadata Leak, API Leak, Punctuation Pattern, OpenAI, Claude, Gemini and Perplexity. Character rows use category badges such as space, bracket, punctuation, invisible format and safe symbol.

Sources are provenance, not proof. A source badge explains where the idea for a rule came from or which public documentation describes that artefact shape. Hover text in the UI and the exported reports include the source name and description so the rule is easier to audit later.

What GlyphGuard does not prove

GlyphGuard is not an authorship detector. It will not prove a person used AI, and it will not prove a person did not use AI.

False positives are expected. Humans write generic transitions. Humans use dashes. Humans paste weird Unicode by accident. False negatives are also expected. AI output can be edited, paraphrased or generated without obvious tool leaks.

Treat hard artefacts differently from weak style hints. A leaked private-use citation marker is strong copy/paste evidence. A generic transition is only a prompt to look closer.

Scores

Scores are for prioritising review, not judging authorship.

  • Raw score adds up weighted matches.
  • Contextual score adjusts the raw evidence using density and spread across the text.
  • Character score covers glyph-level findings.
  • Holistic score covers writing-pattern and tool-artefact rules.
  • Total score combines both.

A short text with one hard citation leak can still score high. A long document with a few mild signals should not be treated the same way. That is why the contextual score exists.

Score bands

Family Clean Trace Low Medium High Very high
Glyph 0 >0 and < 2 >= 2 and < 6 >= 6 and < 20 >= 20 and < 50 >= 50
Holistic 0 >0 and < 1.5 >= 1.5 and < 4 >= 4 and < 10 >= 10 and < 20 >= 20
Total 0 >0 and < 3 >= 3 and < 10 >= 10 and < 30 >= 30 and < 70 >= 70

Raw vs contextual scores

Raw score is the direct evidence total. For glyphs, each detected character occurrence contributes a concern weight. For holistic rules, each matched pattern contributes the pattern weight from HOLISTIC_AI_PATTERN_SPECS.

Contextual score keeps that raw evidence as the anchor, then adjusts it using density and spread. Dense evidence in a short text is more concerning than the same raw score scattered through a long text. Evidence spread across many sentences or paragraphs is also more concerning than one localised match.

This is still a heuristic. It is meant to sort review priority, not produce a probability.

Contextual score maths

The maths uses smooth saturation rather than hard cut-offs. The main idea is that ratios such as "matched words divided by total words" should increase concern quickly at first, then flatten out instead of growing forever. The function used for that is based on exponential decay: Exponential decay. The weighted combination is just a weighted average: Weighted arithmetic mean.

Smooth saturation

For a ratio x and a scale value s, GlyphGuard uses:

$$ S(x, s) = 1 - e^{-(x / s)} $$

Where:

  • x is the observed ratio, such as matched words divided by all words.
  • s is the scale that controls how quickly the curve approaches 1.
  • S(x, s) is the bounded factor used by the contextual multiplier.

Small values of x still count. Very large values do not explode the score.

Glyph raw score

For glyphs, each concern level has a weight:

Concern Weight
None 0
Low 0.5
Medium 1.0
High 2.0
Very high 3.0

The raw glyph score is:

$$ R_g = \sum_i count_i \times weight_i $$

Glyph contextual score

Glyph context uses character density and line spread:

$$ d_g = \frac{flagged\ occurrences}{total\ characters} $$

$$ l_g = \frac{matched\ lines}{total\ lines} $$

Those ratios are converted into saturation factors:

$$ D_g = S(d_g, 0.01) $$

$$ L_g = S(l_g, 0.20) $$

The glyph context factor is:

$$ C_g = 0.80D_g + 0.20L_g $$

The final glyph multiplier and contextual glyph score are:

$$ M_g = 0.70 + 1.30C_g $$

$$ Score_g = R_g \times M_g $$

Holistic raw score

For holistic matches, raw score is the sum of matched pattern weights:

$$ R_h = \sum_j weight_j $$

The scorer only counts matches that overlap prose blocks or blocks with matches. This avoids treating every bit of boilerplate or non-prose file content as equally meaningful.

Holistic contextual score

Holistic context uses word density, sentence spread and paragraph spread:

$$ d_w = \frac{matched\ words}{total\ prose\ words} $$

$$ s_h = \frac{matched\ sentences}{total\ sentences} $$

$$ p_h = \frac{matched\ paragraphs}{total\ paragraphs} $$

Word density uses saturation directly:

$$ W_h = S(d_w, 0.05) $$

Sentence and paragraph spread also get a small-sample confidence factor so one sentence or one paragraph cannot look stronger than it is:

$$ Conf(n, k) = \frac{n}{n + k} $$

$$ S_h = S(s_h, 0.30) \times Conf(total\ sentences, 8.0) $$

$$ P_h = S(p_h, 0.60) \times Conf(total\ paragraphs, 5.0) $$

The holistic context factor is weighted towards word density because paragraph counts can be volatile in short text:

$$ C_h = 0.55W_h + 0.30S_h + 0.15P_h $$

The final multiplier and contextual holistic score are:

$$ M_h = 0.50 + 1.50C_h $$

$$ Score_h = R_h \times M_h $$

Total score

The total score just adds the two families:

$$ R_t = R_g + R_h $$

$$ Score_t = Score_g + Score_h $$

The UI shows contextual score first and raw score in brackets, because contextual score is usually the better review priority.

Detection rule families

The detection rules are defined in glyphguard.py so the app remains self-contained, might expand it in future or allow loading from somewhere in future maybe.

Unicode and hidden-character rules

Glyph-level detections live in TRACKED_CHAR_SPECS until I think of a better name. The app also derives fallback metadata from Python's Unicode database for non-ASCII characters that are not in the curated list. Rules cover invisible controls, private-use markers, bracket variants, whitespace variants, dash and quote variants, fullwidth characters, replacement suggestions and derived unknown Unicode.

The table lists code points and escaped examples rather than inserting every raw character directly into the README. That keeps invisible/private-use characters from hiding inside the documentation itself.

Tracked character table (127 entries)

Code Visibility Concern Categories Description Kind Example Replacement Keyboard HTML/raw
U+0009 Visible Low concern Tracked, Space, Control Tab Tracked character word ? word ? 4 spaces Tab -
U+000A Visible No concern Tracked, Space, Control Line Feed (LF) Tracked character line1\\nline2 keep in double Enter -
U+000C Visible High concern Tracked, Control Form Feed Tracked character - remove - -
U+000D Visible No concern Tracked, Space, Control Carriage Return (CR) Tracked character - keep in double Enter -
U+001C Invisible Very high concern Tracked, Invisible format, Control File Separator Tracked character - remove - -
U+0020 Visible No concern Tracked, Keyboard, Space Regular Space Tracked character word word keep Spacebar -
U+00A0 Visible Medium concern Tracked, Space No-Break Space Tracked character word word ? space - -
U+00A2 Visible No concern Tracked, Safe symbol, Symbol Cent Sign Common safe symbol 50\\xa2 keep - \\xa2
U+00A3 Visible No concern Tracked, Keyboard, Safe symbol, Symbol, GB language char Pound Sign Common safe symbol \\xa310 keep \\xa3 \\xa3
U+00A4 Visible No concern Tracked, Safe symbol, Symbol Currency Sign Common safe symbol \\xa4 keep - \\xa4
U+00A5 Visible No concern Tracked, Safe symbol, Symbol Yen Sign Common safe symbol \\xa51000 keep - \\xa5
U+00A7 Visible No concern Tracked, Safe symbol, Punctuation Section Sign Common safe symbol \\xa7 1 keep - \\xa7
U+00A9 Visible No concern Tracked, Safe symbol, Symbol Copyright Sign Common safe symbol \\xa9 2026 keep - \\xa9
U+00AD Invisible Very high concern Tracked, Invisible format, Control Soft Hyphen Tracked character - remove - \\xad
U+00AE Visible No concern Tracked, Safe symbol, Symbol Registered Sign Common safe symbol Name\\xae keep - \\xae
U+00B0 Visible No concern Tracked, Safe symbol, Symbol Degree Sign Common safe symbol 21\\xb0C keep - \\xb0
U+00B6 Visible No concern Tracked, Safe symbol, Punctuation Pilcrow Sign Common safe symbol \\xb6 keep - \\xb6
U+0192 Visible No concern Tracked, Safe symbol, Letter, International char Latin Small Letter F With Hook / Florin Common safe symbol \\u019210 keep - \\u0192
U+034F Invisible Very high concern Tracked, Invisible format, Mark, International char Grapheme Joiner Tracked character - remove - \\u034f
U+115F Visible Low concern Tracked, Letter, International char Hangul Choseong Filler Tracked character - remove - \\u115f
U+1160 Visible Low concern Tracked, Letter, International char Hangul Jungseong Filler Tracked character - remove - \\u1160
U+1680 Visible Medium concern Tracked, Space Ogham Space Mark Tracked character - ? space - -
U+17B4 Visible Low concern Tracked, Mark, International char Khmer Vowel Inherent AQ Tracked character - remove - \\u17b4
U+17B5 Visible Low concern Tracked, Mark, International char Khmer Vowel Inherent AA Tracked character - remove - \\u17b5
U+180B Invisible Very high concern Tracked, Invisible format, Mark, International char Mongolian VS-1 Tracked character - remove - \\u180b
U+180C Invisible Very high concern Tracked, Invisible format, Mark, International char Mongolian VS-2 Tracked character - remove - \\u180c
U+180D Invisible Very high concern Tracked, Invisible format, Mark, International char Mongolian VS-3 Tracked character - remove - \\u180d
U+180E Visible Medium concern Tracked, Space, Control Mongolian Vowel Sep. Tracked character - ? space - \\u180e
U+2000 Visible Medium concern Tracked, Space En Quad Tracked character - ? space - -
U+2001 Visible Medium concern Tracked, Space Em Quad Tracked character - ? space - -
U+2002 Visible Medium concern Tracked, Space En Space Tracked character - ? space - -
U+2003 Visible Medium concern Tracked, Space Em Space Tracked character - ? space - -
U+2004 Visible Medium concern Tracked, Space Three-Per-Em Space Tracked character - ? space - -
U+2005 Visible Medium concern Tracked, Space Four-Per-Em Space Tracked character - ? space - -
U+2006 Visible Medium concern Tracked, Space Six-Per-Em Space Tracked character - ? space - -
U+2007 Visible Medium concern Tracked, Space Figure Space Tracked character - ? space - -
U+2008 Visible Medium concern Tracked, Space Punctuation Space Tracked character - ? space - -
U+2009 Visible Medium concern Tracked, Space Thin Space Tracked character - ? space - -
U+200A Visible Medium concern Tracked, Space Hair Space Tracked character - ? space - -
U+200B Invisible Very high concern Tracked, Invisible format, Control Zero-Width Space Tracked character - ? space - -
U+200C Invisible Very high concern Tracked, Invisible format, Control Zero-Width Non-Joiner Tracked character - remove - \\u200c
U+200D Invisible Very high concern Tracked, Invisible format, Control Zero-Width Joiner Tracked character - remove - \\u200d
U+200E Invisible Very high concern Tracked, Invisible format, Control Left-To-Right Mark Tracked character - remove - \\u200e
U+200F Invisible Very high concern Tracked, Invisible format, Control Right-To-Left Mark Tracked character - remove - \\u200f
U+2013 Visible Medium concern Tracked, Punctuation En Dash Tracked character pages 1\\u201310 - - \\u2013
U+2014 Visible Medium concern Tracked, Punctuation Em Dash Tracked character word\\u2014word -- or - - \\u2014
U+2018 Visible Medium concern Tracked, Punctuation Left Single Quote Tracked character 'hello ' - \\u2018
U+2019 Visible Medium concern Tracked, Punctuation Right Single Quote Tracked character don't ' - \\u2019
U+201C Visible Medium concern Tracked, Punctuation Left Double Quote Tracked character "hello " - \\u201c
U+201D Visible Medium concern Tracked, Punctuation Right Double Quote Tracked character hello" " - \\u201d
U+2026 Visible Medium concern Tracked, Punctuation Horizontal Ellipsis Tracked character wait... ... - \\u2026
U+2028 Invisible Very high concern Tracked, Invisible format Line Separator Tracked character - ? \n - -
U+2029 Invisible Very high concern Tracked, Invisible format Paragraph Separator Tracked character - ? \n\n - -
U+202A Invisible Very high concern Tracked, Invisible format, Control LTR Embedding Tracked character - remove - \\u202a
U+202B Invisible Very high concern Tracked, Invisible format, Control RTL Embedding Tracked character - remove - \\u202b
U+202C Invisible Very high concern Tracked, Invisible format, Control Pop Directional Fmt Tracked character - remove - \\u202c
U+202D Invisible Very high concern Tracked, Invisible format, Control LTR Override Tracked character - remove - \\u202d
U+202E Invisible Very high concern Tracked, Invisible format, Control RTL Override Tracked character - remove - \\u202e
U+202F Visible Medium concern Tracked, Space Narrow NBSP Tracked character - ? space - -
U+2045 Visible Medium concern Tracked, Bracket, Punctuation Left Square Bracket With Quill Bracket alternative \\u2045text [ [ \\u2045
U+2046 Visible Medium concern Tracked, Bracket, Punctuation Right Square Bracket With Quill Bracket alternative text\\u2046 ] ] \\u2046
U+205F Visible Medium concern Tracked, Space Math Space Tracked character - ? space - -
U+2060 Invisible Very high concern Tracked, Invisible format, Control Word Joiner Tracked character - remove - \\u2060
U+2061 Invisible Very high concern Tracked, Invisible format, Control Function Application Tracked character - remove - \\u2061
U+2062 Invisible Very high concern Tracked, Invisible format, Control Invisible Times Tracked character - ? "x" - \\u2062
U+2063 Invisible Very high concern Tracked, Invisible format, Control Invisible Separator Tracked character - ? "," - \\u2063
U+2064 Invisible Very high concern Tracked, Invisible format, Control Invisible Plus Tracked character - ? "+" - \\u2064
U+2066 Invisible Very high concern Tracked, Invisible format, Control LTR Isolate Tracked character - remove - \\u2066
U+2067 Invisible Very high concern Tracked, Invisible format, Control RTL Isolate Tracked character - remove - \\u2067
U+2068 Invisible Very high concern Tracked, Invisible format, Control First Strong Isolate Tracked character - remove - \\u2068
U+2069 Invisible Very high concern Tracked, Invisible format, Control Pop Directional Isolate Tracked character - remove - \\u2069
U+206A Invisible Very high concern Tracked, Invisible format, Control Inhibit Symmetric Swap Tracked character - remove - \\u206a
U+206B Invisible Very high concern Tracked, Invisible format, Control Activate Symmetric Swap Tracked character - remove - \\u206b
U+206C Invisible Very high concern Tracked, Invisible format, Control Inhibit Arabic Form Shaping Tracked character - remove - \\u206c
U+206D Invisible Very high concern Tracked, Invisible format, Control Activate Arabic Form Shaping Tracked character - remove - \\u206d
U+206E Invisible Very high concern Tracked, Invisible format, Control National Digit Shapes Tracked character - remove - \\u206e
U+206F Invisible Very high concern Tracked, Invisible format, Control Nominal Digit Shapes Tracked character - remove - \\u206f
U+20A1 Visible No concern Tracked, Safe symbol, Symbol Colon Sign Common safe symbol \\u20a110 keep - \\u20a1
U+20A6 Visible No concern Tracked, Safe symbol, Symbol Naira Sign Common safe symbol \\u20a610 keep - \\u20a6
U+20A9 Visible No concern Tracked, Safe symbol, Symbol Won Sign Common safe symbol \\u20a91000 keep - \\u20a9
U+20AA Visible No concern Tracked, Safe symbol, Symbol New Sheqel Sign Common safe symbol \\u20aa10 keep - \\u20aa
U+20AB Visible No concern Tracked, Safe symbol, Symbol Dong Sign Common safe symbol \\u20ab10 keep - \\u20ab
U+20AC Visible No concern Tracked, Safe symbol, Symbol Euro Sign Common safe symbol \\u20ac10 keep - \\u20ac
U+20AD Visible No concern Tracked, Safe symbol, Symbol Kip Sign Common safe symbol \\u20ad10 keep - \\u20ad
U+20B1 Visible No concern Tracked, Safe symbol, Symbol Peso Sign Common safe symbol \\u20b110 keep - \\u20b1
U+20B2 Visible No concern Tracked, Safe symbol, Symbol Guarani Sign Common safe symbol \\u20b210 keep - \\u20b2
U+20B4 Visible No concern Tracked, Safe symbol, Symbol Hryvnia Sign Common safe symbol \\u20b410 keep - \\u20b4
U+20B9 Visible No concern Tracked, Safe symbol, Symbol Indian Rupee Sign Common safe symbol \\u20b9500 keep - \\u20b9
U+20BA Visible No concern Tracked, Safe symbol, Symbol Turkish Lira Sign Common safe symbol \\u20ba10 keep - \\u20ba
U+20BD Visible No concern Tracked, Safe symbol, Symbol Ruble Sign Common safe symbol \\u20bd100 keep - \\u20bd
U+20BF Visible No concern Tracked, Safe symbol, Symbol Bitcoin Sign Common safe symbol \\u20bf1 keep - \\u20bf
U+2122 Visible No concern Tracked, Safe symbol, Symbol Trade Mark Sign Common safe symbol Name\\u2122 keep - \\u2122
U+27E6 Visible Medium concern Tracked, Bracket, Punctuation Mathematical Left White Square Bracket Bracket alternative \\u27e6text [ [ \\u27e6
U+27E7 Visible Medium concern Tracked, Bracket, Punctuation Mathematical Right White Square Bracket Bracket alternative text\\u27e7 ] ] \\u27e7
U+27E8 Visible Medium concern Tracked, Bracket, Punctuation Mathematical Left Angle Bracket Bracket alternative \\u27e8text < < \\u27e8
U+27E9 Visible Medium concern Tracked, Bracket, Punctuation Mathematical Right Angle Bracket Bracket alternative text\\u27e9 > > \\u27e9
U+2800 Visible Medium concern Tracked, Space, Symbol Braille Blank Tracked character - ? space - \\u2800
U+3000 Visible Medium concern Tracked, Space Ideographic Space Tracked character - ? space - -
U+3010 Visible Medium concern Tracked, Bracket, Punctuation Left Black Lenticular Bracket Bracket alternative \\u3010text [ [ \\u3010
U+3011 Visible Medium concern Tracked, Bracket, Punctuation Right Black Lenticular Bracket Bracket alternative text\\u3011 ] ] \\u3011
U+3016 Visible High concern Tracked, Bracket, Punctuation Left White Lenticular Bracket AI citation bracket alternative \\u30166\\u2020L9-L11 [ [ \\u3016
U+3017 Visible High concern Tracked, Bracket, Punctuation Right White Lenticular Bracket AI citation bracket alternative 6\\u2020L9-L11\\u3017 ] ] \\u3017
U+3164 Visible Medium concern Tracked, Space, Letter, International char Hangul Filler Tracked character - ? space - \\u3164
U+E200 Visible Very high concern Tracked, Control Private Use Citation Start OpenAI citation marker \\ue200cite remove - \\ue200
U+E201 Visible Very high concern Tracked, Control Private Use Citation Stop OpenAI citation marker \\ue201 remove - \\ue201
U+E202 Visible Very high concern Tracked, Control Private Use Citation Delimiter OpenAI citation marker \\ue202 remove - \\ue202
U+FE00 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-1 Tracked character - remove - \\ufe00
U+FE01 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-2 Tracked character - remove - \\ufe01
U+FE02 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-3 Tracked character - remove - \\ufe02
U+FE03 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-4 Tracked character - remove - \\ufe03
U+FE04 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-5 Tracked character - remove - \\ufe04
U+FE05 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-6 Tracked character - remove - \\ufe05
U+FE06 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-7 Tracked character - remove - \\ufe06
U+FE07 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-8 Tracked character - remove - \\ufe07
U+FE08 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-9 Tracked character - remove - \\ufe08
U+FE09 Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-10 Tracked character - remove - \\ufe09
U+FE0A Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-11 Tracked character - remove - \\ufe0a
U+FE0B Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-12 Tracked character - remove - \\ufe0b
U+FE0C Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-13 Tracked character - remove - \\ufe0c
U+FE0D Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-14 Tracked character - remove - \\ufe0d
U+FE0E Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-15 Tracked character - remove - \\ufe0e
U+FE0F Invisible Very high concern Tracked, Invisible format, Mark, International char Variation Selector-16 Tracked character - remove - \\ufe0f
U+FEFF Invisible Very high concern Tracked, Invisible format, Control Zero-Width NBSP / BOM Tracked character - remove - \\ufeff
U+FF3B Visible Medium concern Tracked, Bracket, Punctuation Fullwidth Left Square Bracket Bracket alternative \\uff3btext [ [ \\uff3b
U+FF3D Visible Medium concern Tracked, Bracket, Punctuation Fullwidth Right Square Bracket Bracket alternative text\\uff3d ] ] \\uff3d
U+FFA0 Visible Medium concern Tracked, Space, Letter, International char Half-width Hangul Filler Tracked character - ? space - \\uffa0
U+FFFC Visible High concern Tracked, Symbol Object Replacement Tracked character - ? "[OBJECT]" - \\ufffc

Holistic AI pattern rules

The holistic rule sections below line up with the section comments in glyphguard.py. The summary tables include the rule metadata used by the app. Regexes are in collapsible blocks after each table because putting the full patterns directly in the table makes the README almost unreadable.

Prose and style heuristics

General prose, style and structure heuristics from the first rule block in glyphguard.py.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
ai_vocabulary (?i)\b(tapestry|delve|nuance|multifaceted|leverage|holistic|seamless|comprehensive|robust|foster|garner|intricate|poignant|vibrant)\b medium 1.0 LLM Style, Word Choice wikipedia_signs_ai_writing Generic high-polish vocabulary associated with LLM prose. Significance and promotional terms are split into more precise sibling rules. Prefer concrete wording and remove decorative vocabulary when it adds no meaning. The report delves into a vibrant tapestry of ideas.
The proposal offers a holistic and multifaceted view.
rule_of_three \b\w{3,},\s+\w{3,},\s+and\s+\w{3,}\b low 0.5 Structure, List Pattern wikipedia_signs_ai_writing Three-item list structure that generated prose can overuse. Vary list structures or restructure the sentence. Clearer, faster, and safer delivery matters.
Teams need planning, testing, and monitoring.
negative_parallelism (?i)\b(?:it\s+is|it'?s)\s+not\s+(?:just|merely|only|simply)\s+[^.?!\n]{1,120}?(?:,|;|\u2014|--|\s+but\s+(?:also|rather|instead)\b) high 2.0 Structure, Rhetorical Pattern wikipedia_signs_ai_writing Formulaic not-just contrast framing. State the contrast directly without the not-just formula. It's not just speed, it's reliability.
It is not merely a tool, but also a platform.
copula_avoidance (?i)\b(?:serves?\s+as|stands?\s+as|represents?\s+(?:an?\s+)?(?:important|major|significant|key|pivotal|crucial)?\s*\w+|marks?\s+(?:an?\s+)?(?:important|major|significant|key|pivotal|crucial|historic)\s+\w+|holds?\s+the\s+distinction\s+of)\b medium 1.5 Grammar, Marketing Style, Low Specificity wikipedia_signs_ai_writing Ornate replacements for simple is/are descriptions, narrowed to puffery-like copula substitutes. Prefer direct descriptions where ornate framing adds no evidence. The gallery serves as the organisation\u2019s exhibition space.
The release marks a significant milestone for the project.
prestige_boast_framing (?i)\b(?:boasts?\s+(?:an?\s+)?(?:impressive|extensive|rich|diverse|robust|unique)?\s*\w+|renowned\s+for|celebrated\s+as|acclaimed\s+for|recognised\s+as|recognized\s+as)\b high 2.0 Marketing Style, Low Specificity wikipedia_signs_ai_writing Prestige or boast framing separated from generic promotional buzzwords. Replace prestige framing with verifiable facts. The platform boasts extensive analytics.
The artist is renowned for a unique visual language.
promotional_buzzwords (?i)\b(?:groundbreaking|cutting[-\s]?edge|state[-\s]?of[-\s]?the[-\s]?art|game[-\s]?changer|revolutionary|breathtaking|stunning|unprecedented|unparalleled|transformative|disruptive|innovative\s+(?:solution|approach))\b high 2.0 Marketing Style, Low Specificity wikipedia_signs_ai_writing Standalone promotional buzzwords, separated from copula/boast framing. Use specific, concrete descriptions instead of buzzwords. This groundbreaking solution changes the market.
The tool offers a state-of-the-art approach.
vague_attribution (?i)\b(?:industry\s+experts\s+(?:say|believe|agree)|observers\s+(?:note|suggest|point)|critics\s+(?:argue|contend|suggest)|some\s+(?:argue|say|believe|suggest)|many\s+(?:believe|argue|contend|suggest)|it\s+is\s+widely\s+(?:believed|accepted|recognized|recognised|acknowledged)|research\s+(?:shows|suggests|indicates|demonstrates)\s+that)\b high 2.0 Evidence Gap, Attribution wikipedia_signs_ai_writing Vague attribution without a named source or citation. Cite a specific source or remove the claim. Industry experts say adoption will grow.
Research suggests that teams are more productive.
collaborative_artifact (?i)\b(?:I\s+hope\s+this\s+helps|let\s+me\s+know\s+if|would\s+you\s+like\s+(?:me\s+)?to|of\s+course!|certainly!|absolutely!|I'?d\s+be\s+(?:happy|glad|pleased)\s+to\s+help|is\s+there\s+anything\s+else|feel\s+free\s+to\s+ask)\b critical 3.0 Chatbot Artefact wikipedia_signs_ai_writing Chatbot conversation artefacts left in final text. Remove these phrases entirely. I hope this helps.
Let me know if you want changes.
formal_filler_phrase (?i)\b(?:in\s+order\s+to|due\s+to\s+the\s+fact\s+that|at\s+this\s+point\s+in\s+time|without\s+(?:a\s+)?doubt|needless\s+to\s+say|last\s+but\s+not\s+least)\b medium 1.0 LLM Style, Filler wikipedia_signs_ai_writing Formal filler phrases that add bulk without much information. Cut the phrase and start directly with the main point. In order to deploy, run the script.
Due to the fact that costs vary, estimates changed.
ing_superficial \b\w+ing,\s+(?:ensuring|reflecting|highlighting|underscoring|emphasizing|emphasising|demonstrating|illustrating|showcasing)\b high 2.0 Structure, Rhetorical Pattern wikipedia_signs_ai_writing Superficial -ing analysis clause used as a shortcut. Replace with direct statements about cause, effect or evidence. The update improves caching, ensuring faster loads.
The rollout supports scaling, highlighting adoption.
negative_conclusion (?i)\b(?:despite\s+(?:these\s+|the\s+|some\s+)?\w*\s*challenges?|faces?\s+several\s+challenges?|moving\s+forward|looking\s+ahead|going\s+forward|as\s+we\s+(?:move|look)\s+(?:forward|ahead))\b medium 1.0 Essay Style, Structure wikipedia_signs_ai_writing Generic challenge/future-outlook framing used to sound balanced. Be specific about the challenge or future action. Despite these challenges, the project continued.
Looking ahead, the team plans to iterate.
hedging (?i)\b(?:it\s+(?:seems|appears)\s+(?:that|to\s+be)|arguably|one\s+might\s+(?:say|argue|suggest)|it\s+could\s+be\s+(?:said|argued)\s+that|in\s+(?:many|some)\s+(?:ways|respects)|to\s+(?:some|a\s+(?:certain|large))\s+extent)\b medium 1.0 Cautious Style, Low Specificity wikipedia_signs_ai_writing Hedging language that weakens or genericises claims. State the claim directly when evidence supports it. It seems that the model is improving.
Arguably, the trade-off is acceptable.
conclusion_transition (?i)\b(?:in\s+conclusion|to\s+sum\s+up|wrapping\s+up|to\s+(?:recap|recapitulate)|all\s+in\s+all|in\s+(?:the\s+)?final\s+analysis)\b medium 1.5 Essay Style, Structure, Transition wikipedia_signs_ai_writing Formulaic ending transitions. Summary-specific variants are split into section_summary_transition. End naturally or use a specific summary point. In conclusion, the change worked.
To sum up, the release was successful.
conjunctive_adverb_overuse (?i)\b(?:furthermore|moreover|additionally|consequently|nevertheless|nonetheless|notwithstanding|whereas|hereby|thereof|therein|wherein)\b medium 1.5 Formal Style, Transition wikipedia_signs_ai_writing Formal conjunctive adverbs that can make prose mechanically academic. Use simpler transitions where clearer. Furthermore, the API supports retries.
Nevertheless, latency remains a risk.
universal_claim (?i)\b(?:everyone\s+(?:knows|agrees|understands)|no\s+one\s+(?:can\s+)?deny|it'?s?\s+universally\s+(?:accepted|acknowledged)|we\s+all\s+(?:know|agree|understand)|across\s+the\s+(?:board|globe|world))\b high 2.0 Evidence Gap, Overclaim wikipedia_signs_ai_writing Universal generalisations used to imply consensus without evidence. Qualify the claim or provide evidence. Everyone knows this approach is best.
No one can deny the impact.
numbered_benefits (?i)\b(?:three|3|four|4|five|5)\s+(?:key|main|primary|major|significant)\s+(?:benefits|advantages|reasons|factors|ways)\b medium 1.5 List Pattern, Structure wikipedia_signs_ai_writing Listicle-style numbered benefit or reason framing. Introduce benefits naturally instead of announcing the count. Three key benefits are speed, safety, and cost.
5 main reasons explain the change.

Citation and tool-output leaks

OpenAI citation, source, prompt and UI leak rules.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
openai_browser_line_citation (?:\u3010|\u3016)\s*\d+\s*\u2020\s*L\d+(?:-L\d+)?\s*(?:\u3011|\u3017) critical 5.0 Tool Artefact, Citation Leak, OpenAI, ChatGPT, Harmony openai_harmony_docs Raw OpenAI browser, Deep Research or Harmony-style line citation marker. Render the citation properly or strip the marker. See the source here \u30166\u2020L9-L11\u3017.
The claim appears at \u30109\u2020L9-L11\u3011.
openai_assistants_source_placeholder (?:\u3010|\u3016)\s*\d+:\d+\s*[\u2020\u2021]\s*[^\u3011\u3017\r\n]{1,120}\s*(?:\u3011|\u3017) critical 5.0 Tool Artefact, Citation Leak, OpenAI, Assistants, File Search stackoverflow_openai_assistants_placeholder Raw OpenAI Assistants/File Search placeholder citation. Replace with a visible citation or strip it. Raw marker \u30164:2\u2020source\u3017 appeared.
Another marker was \u301012:1\u2021source\u3011.
openai_private_use_citation \uE200cite\uE202(?:[A-Za-z][\w.-]*|turn(?:\d+|X)\w*(?:\d+|Y)?)(?:\uE202(?:L\d+(?:-L\d+)?|P(?:aragraph)?\s*\d+|[A-Za-z][\w.-]*|turn\d+\w+\d+))*\uE201 critical 5.0 Tool Artefact, Citation Leak, OpenAI, API openai_citation_formatting_docs, stackoverflow_openai_assistants_placeholder Raw OpenAI private-use Unicode citation marker. Remove or render the citation properly. \uE200cite\uE202turn0file1\uE202L8-L13\uE201
\uE200cite\uE202turnXfileY\uE201
openai_private_use_generic_marker \ue200(?!cite\ue202)[^\r\n\ue201]{1,240}\ue202[^\r\n\ue201]{1,240}\ue201 critical 4.5 Tool Artefact, Citation Leak, OpenAI, ChatGPT openai_citation_formatting_docs, observed_chatgpt_content_reference Raw OpenAI/ChatGPT private-use UI marker with an internal delimiter. Remove or render the marker properly. \uE200filecite\uE202turn0file0\uE201
\uE200products\uE202{"id":1}\uE201
openai_content_reference_marker :contentReference\[(?:oaicite|[^\]\s:]+):\d+\]\{index=\d+\} critical 4.0 Tool Artefact, Citation Leak, OpenAI, ChatGPT wikipedia_signs_ai_writing, observed_chatgpt_content_reference Raw ChatGPT/OpenAI contentReference citation marker. Remove or render the reference properly. :contentReference[oaicite:3]{index=3}
:contentReference[source:17]{index=17}
openai_naked_source_id (?<!\ue202)\bturn\d+(?:file|search|news|url|block|image|product|finance|forecast|sports|time)\d+\b high 3.0 Tool Artefact, Citation Leak, OpenAI, API openai_citation_formatting_docs, wikipedia_signs_ai_writing OpenAI-style source reference ID leaked without its citation wrapper. Check whether a raw citation/source ID leaked. turn0file0
turn2search5
openai_harmony_special_token <\|(?:start|end|message|channel|constrain|call|return)\|> critical 5.0 Tool Artefact, Prompt Leak, OpenAI, Harmony openai_harmony_docs OpenAI Harmony prompt/message special token leaked into visible text. Remove internal prompt-format tokens. <|start|>
<|message|>
openai_tool_channel_leak \bValid channels:\s*analysis\s*,\s*(?:commentary\s*,\s*)?final\b|\bto=(?:functions|browser|python|web|file_search|image_gen|automations)\.[A-Za-z_]\w*\b critical 4.0 Tool Artefact, Prompt Leak, OpenAI, Tool use openai_harmony_docs OpenAI-style tool/channel instruction leaked into visible text. Remove internal tool-call or channel-routing text. Valid channels: analysis, commentary, final
to=functions.get_current_weather

Metadata and API leaks

Cross-vendor API and grounding metadata rules.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
openai_file_citation_json "type"\s*:\s*"file_citation"|"annotations"\s*:\s*\[|"file_id"\s*:\s*"file-[A-Za-z0-9_-]+ high 3.0 Metadata Leak, API Leak, OpenAI, File Search openai_file_search_docs Raw OpenAI Responses/File Search citation annotation JSON. Treat as API/debug output rather than final prose. "type": "file_citation"
"file_id": "file-abc123"
anthropic_citation_json "cited_text"\s*:|"document_index"\s*:|"start_char_index"\s*:|"end_char_index"\s*:|"start_page_number"\s*:|"end_page_number"\s*:|"start_block_index"\s*:|"end_block_index"\s*: high 2.5 Metadata Leak, API Leak, Anthropic, Claude anthropic_claude_citations_docs Raw Anthropic/Claude citation metadata. Treat as API metadata rather than final prose. "cited_text": "..."
"start_char_index": 42
gemini_grounding_metadata_json "groundingMetadata"\s*:|"webSearchQueries"\s*:|"groundingChunks"\s*:|"groundingSupports"\s*:|"groundingChunkIndices"\s*:|"searchEntryPoint"\s*: high 2.5 Metadata Leak, API Leak, Google, Gemini google_gemini_grounding_docs Raw Gemini grounding/citation metadata. Treat as Gemini API grounding metadata. "groundingMetadata": {}
"webSearchQueries": ["x"]
perplexity_search_metadata_json "citations"\s*:\s*\[|"search_results"\s*:\s*\[|"search_context_size"\s*: medium 2.0 Metadata Leak, API Leak, Perplexity, Sonar perplexity_sonar_features_docs Raw Perplexity/Sonar search or citation metadata. Treat as API/search metadata rather than final prose. "citations": ["https://example.com"]
"search_context_size": "low"

Model/vendor-specific artefacts

Model/vendor-specific rules where the marker is narrow enough to tag.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
openai_creature_metaphor_tic (?i)\b(?:(?:little|tiny|small|feral|chaos|formatting|file[-\s]?format|filesystem|spreadsheet|cache|parser|prompt|token|tool|build|deploy(?:ment)?|perf|regex|yaml|json|state[-\s]?machine|clipboard|merge|git|branch|tooltip|ui|ux)\s+(?:goblin|goblins|gremlin|gremlins|troll|trolls|ogre|ogres|raccoon|raccoons|pigeon|pigeons)|(?:goblin|gremlin|troll|ogre|raccoon|pigeon)[-\s]?(?:mode|math|brain|energy|gearbox|bandwidth|version|logic))\b high 3.0 LLM Style, Word Choice, LLM Metaphor, OpenAI, Codex, GPT-5.1+, GPT-5.4-Nerdy, GPT-5.5-Codex openai_goblin_article, tidbits_gpt55_prompt_discussion Creature-metaphor lexical tic associated with OpenAI GPT-5.1+ / Codex style leakage. Review whether the metaphor is intentional. The filesystem goblin ate the config.
There is a little perf gremlin running unattended.

Dash-delimited parenthetical asides

Dash-aside punctuation heuristics kept separate because they are noisy unless repeated.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
unicode_dash_delimited_parenthetical (?<!\S)(?:\u2014\s*[^\n\u2014]{8,180}\s*\u2014|\u2013\s*[^\n\u2013]{8,180}\s*\u2013)(?!\S) medium 2.0 Punctuation Pattern, Parenthetical Aside, Structure wikipedia_signs_ai_writing Unicode dash-delimited parenthetical aside using paired em/en dashes. Consider commas, parentheses or a separate sentence. The parser \u2014 somehow still? alive \u2014 handled the payload.
The model \u2013 trying to sound casual \u2013 added another aside.
ascii_dash_delimited_parenthetical (?<!\S)(?:--\s*(?=[^\n]{8,180}?\s*--)(?=[^\n]*[A-Za-z])(?:(?!--)[^\n]){8,180}?\s*--|-\s+(?=[^\n]{8,180}?\s+-)(?=[^\n]*[A-Za-z])(?:(?!\s+-)[^\n]){8,180}?\s+-)(?!\S) low 1.25 Punctuation Pattern, Parenthetical Aside, Structure wikipedia_signs_ai_writing ASCII dash-delimited parenthetical aside. Weak structural signal unless it appears with other markers. The cache -- because, naturally, it hates us -- lied.
The UI - in its infinite wisdom - ate the tooltip.

Wikipedia lead, prose and formatting heuristics

Rules for Wikipedia-style lead, prose and formatting patterns.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
leads_list_title_proper_noun (?im)^\s*(?:The\s+)?(?:[\u201C"]?List\s+of\s+[^\n.]{3,120}[\u201D"]?|[A-Z][A-Za-z0-9\u2019\'-]+(?:\s+[A-Z][A-Za-z0-9\u2019\'-]+){0,4}\s+editions)\s+(?:is|are|refers\s+to|constitutes|comprises)\b medium 1.0 Structure, Lead wikipedia_signs_ai_writing Lead sentence treats a list/article title as a standalone proper noun. Introduce lists and descriptive titles naturally. The \u201CList of songs about Mexico\u201D is a curated compilation of musical works.
EuroGames editions is the chronological list of the biennial EuroGames.
negative_parallelism_not_only (?i)\bnot\s+only\b(?:[^.?!\n]{0,180}\bbut\s+(?:also|rather|instead)\b)? medium 1.5 Structure, Rhetorical Pattern wikipedia_signs_ai_writing Broad not only / but also parallelism marker. Use direct comparison or contrast where possible. This choice is not only dismissive but also harsh.
The portrait constitutes not only self-representation but a visual document.
negative_parallelism_not_but (?i)\b(?:it\s+is|it'?s|this\s+is|that\s+is)?\s*not\s+(?:an?\s+)?[^.?!\n\u2014-]{1,90}?\s+(?:but|rather)\b|\bnot\s+[^.?!\n\u2014-]{1,40}?,\s*not\s+[^.?!\n\u2014-]{1,40}?,\s*not\s+[^.?!\n\u2014-]{1,80}?(?:\u2014|--|,)?\s*(?:just|only|rather|but)\b high 2.0 Structure, Rhetorical Pattern wikipedia_signs_ai_writing Separate negative-parallelism shape for not X but Y and repeated not-list contrast. Avoid formulaic rejection/reframe statements. It is not a career but an algorithmic moment.
Not a career, not a body of work, not sustained relevance \u2014 just an algorithmic moment.
title_case_headings (?m)^\s*(?:#{1,6}\s*)?(?=[^\n]{12,120}$)(?:[A-Z][A-Za-z0-9\u2019\'-]+|of|and|the|for|in|on|to|with)(?:\s+(?:[A-Z][A-Za-z0-9\u2019\'-]+|of|and|the|for|in|on|to|with)){2,}\s*$ none 0.25 No Concern, Headings, Formatting wikipedia_signs_ai_writing Title Case heading-like line. Added as an opt-in/no-concern review signal because this is noisy outside heading context. Only flag if the user opts into no-concern/low-confidence style review. Impact of Technology and Digitalization
Sustainable Development and Environmental Law
boldface_inline_header_list (?m)^\s*(?:[-*\u2022]|\d+[.)])\s+(?:(?:'''[^'\n:]{2,80}:?'''|\*\*[^*\n:]{2,80}:?\*\*)|(?:'''[^'\n]{2,80}'''|\*\*[^*\n]{2,80}\*\*)\s*:)\s+ medium 1.5 Structure, List Pattern, Formatting wikipedia_signs_ai_writing List item begins with a bold inline header and colon. Use proper list markup and avoid slide-deck style inline headers. 1. **Historical context:** The world was changing after WWII.
- **Conflict of interest:** Please review the sourcing.
boldface_repeated_spans (?s)(?:'''[^'\n]{2,120}'''|\*\*[^*\n]{2,120}\*\*)(?:(?!\n\s*\n).){0,300}(?:'''[^'\n]{2,120}'''|\*\*[^*\n]{2,120}\*\*) low 1.0 Formatting, Low Specificity wikipedia_signs_ai_writing Multiple bold spans inside one paragraph or compact block. Use emphasis sparingly. **Background** text continues, then **Impact** appears in the same paragraph.
'''History''' is followed by prose and '''Reception''' in one paragraph.
inline_header_list (?m)^\s*(?:[-*\u2022#]|\d+[.)])\s+(?!'''|\*\*)(?:[A-Z][^:\n]{2,80})\s*:\s+ medium 1.0 Structure, List Pattern wikipedia_signs_ai_writing List item with a colon-headed mini-section. Integrate short items into prose or use proper headings/lists. \u2022 Historical context: The world was changing after WWII.
1. Historical context post-WWII era: The world was rapidly changing.
thematic_break_heading (?m)^\s*----\s*\n\s*==+\s*[^\n=]+\s*==+ medium 1.0 Structure, Markup, Wikitext wikipedia_signs_ai_writing Markdown-like thematic break immediately before a wikitext heading. Remove extraneous horizontal rules before headings. ----<br>== History ==
----<br>=== Form and construction ===

Chatbot disclaimers, placeholders and renderer artefacts

Rules for copied chatbot disclaimers, placeholder text, renderer leaks and tracking parameters.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
knowledge_cutoff_disclaimer (?i)\b(?:as\s+of\s+(?:my\s+)?(?:last\s+)?(?:knowledge|training)\s+update|up\s+to\s+my\s+last\s+training\s+update|my\s+knowledge\s+(?:only\s+)?(?:goes|extends)\s+(?:up\s+)?to)\b critical 2.5 Chatbot Artefact, Evidence Gap, Disclaimer wikipedia_signs_ai_writing Explicit model knowledge/training cutoff disclaimer. Remove chatbot-cutoff text and verify current sources. As of my last knowledge update in January 2022, I do not have current information.
Up to my last training update, this was not listed.
source_scarcity_disclaimer (?i)\b(?:while\s+specific\s+(?:details|information)\s+(?:is|are)\s+(?:limited|scarce)|not\s+widely\s+(?:available|documented|disclosed)|based\s+on\s+available\s+information|maintains\s+a\s+low\s+profile|keeps\s+personal\s+details\s+private)\b medium 1.5 Evidence Gap, Source Scarcity, Disclaimer wikipedia_signs_ai_writing Source-scarcity speculation often used to pad missing evidence. State only documented information or mark gaps explicitly. Specific details are not widely documented in available sources.
Based on available information, the village has limited coverage.
placeholder_text (?i)(?:\[\s*(?:Describe|Enter|Your Name|specify|Provide)[^\]\n]{0,120}\]|\b(?:INSERT|PASTE|SOURCE|URL|ACCESS[_-]DATE|DATE)_[A-Z0-9_]+\b|\b\d{4}-(?:XX|xx)-(?:XX|xx)\b) critical 2.5 Placeholder, Template, Chatbot Artefact wikipedia_signs_ai_writing Template placeholder or fill-in-the-blank token left in final text. Replace placeholders with real content or remove them. [Describe the specific section that needs editing]
SOURCE_URL_30 and access-date=2025-XX-XX remain in the citation.
openai_oai_citation_renderer_leak (?i)(?:(?<!:)contentReference\[oaicite:\d+\](?!\{index=\d+\})|oai_citation|attached_file:\d+|Example\+\d+) critical 4.0 Tool Artefact, Citation Leak, OpenAI, UI marker observed_chatgpt_content_reference, wikipedia_signs_ai_writing OpenAI-ish citation renderer or attachment marker leak not covered by the stricter contentReference rule. Remove renderer artefacts or replace with proper citations. contentReference[oaicite:3]
attached_file:2 was left in the copied answer.
grok_citation_renderer_leak (?i)\b(?:grok_card|grok_render_citation_card_json|referrer=grok\.com)\b high 3.5 Tool Artefact, Citation Leak, Grok, UI marker wikipedia_signs_ai_writing Grok/X citation renderer artefact. Strip renderer metadata and add normal citations. grok_card appeared in the copied text.
grok_render_citation_card_json was pasted into the article.
triple_colon_writing_block (?m)^:::[^\W\d_][\w-]*\{[^}\n]*(?:variant|variante|id)\s*= critical 4.0 Tool Artefact, UI marker, Chatbot Artefact observed_chatgpt_content_reference ChatGPT writing-block fence leaked into text, including non-English block names. Remove the UI wrapper and keep only the intended content. :::writing{variant="document" id="12345"}
:::\u00E9criture{variante="document" id="67890"}
ai_referral_url_parameter (?i)\b(?:utm_source|utm_medium|utm_campaign|referrer)=(?:openai|chatgpt(?:\.com)?|copilot(?:\.microsoft\.com|\.com)?|grok(?:\.com)?|perplexity|claude|anthropic|gemini|bard)\b high 3.5 URL Parameter, Citation Leak, Tool Artefact wikipedia_signs_ai_writing AI-tool referral/tracking parameter copied with a URL. Strip tracking parameters and verify the source URL. https://example.com/?utm_source=chatgpt.com
https://x.example/?referrer=grok.com
generic_utm_parameter (?i)\b(?:utm_source|utm_medium|utm_campaign|utm_term|utm_content)=([^\s&#]+) low 0.5 URL Parameter, Tracking Parameter wikipedia_signs_ai_writing Generic UTM tracking parameter. Not AI-specific, but useful citation hygiene. Remove tracking parameters when preserving citations. https://example.com/?utm_source=newsletter
https://site.test/?utm_campaign=spring

Wikipedia talk-page, AfC and edit-summary artefacts

Rules for talk-page phrasing, subject-line comments and edit-summary style artefacts.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
canned_quality_goodfaith (?i)\b(?:aligns?\s+with\s+Wikipedia(?:['\u2019]s)?\s+(?:aim|goal|mission|standards)|adheres?\s+to\s+(?:good\s+faith|neutrality|Wikipedia(?:['\u2019]s)?\s+principles)|assume\s+good\s+faith|constructive\s+and\s+collaborative)\b low 0.75 Talk Page, Good Faith, Formal Style wikipedia_signs_ai_writing Canned talk-page good-faith or quality language. Use only where it is genuinely part of the discussion. This aligns with Wikipedia\u2019s mission.
Please assume good faith and keep the discussion constructive.
canned_constructive_criticism (?i)\b(?:if\s+you\s+have\s+any\s+(?:concerns|suggestions|feedback)|if\s+there\s+are\s+any\s+issues|constructive\s+(?:criticism|feedback)|please\s+feel\s+free\s+to\s+(?:improve|comment|respond))\b low 0.5 Talk Page, Constructive Criticism, Chatbot Artefact wikipedia_signs_ai_writing Generic constructive-feedback boilerplate. Keep only if it adds context to the conversation. If you have any concerns, please let me know.
Constructive feedback is welcome.
canned_focus_on_content (?i)\b(?:let\s*'?s\s+focus\s+on\s+(?:improving\s+)?(?:the\s+)?(?:article|content)|in\s+the\s+spirit\s+of\s+collaboration|focus\s+on\s+content\s+rather\s+than\s+contributors)\b low 0.5 Talk Page, Focus Shift wikipedia_signs_ai_writing Canned talk-page focus-shift wording. Use specific article/content issues instead of generic moderation language. Let's focus on improving the article.
Please focus on content rather than contributors.
subject_line_comment (?m)^\s*Subject:\s+[^\n]{1,160}$ low 0.75 Talk Page, Subject Line, Template wikipedia_signs_ai_writing Email/form-style subject line left inside a comment. Remove the subject line or convert it into a heading only if appropriate. Subject: Request for review
Subject: Sources for this article

Wikitext markup, citation-quality and category artefacts

Rules for small tables, headings, categories, reference reuse, book citation pages and policy shortcut clusters.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
unusual_small_table (?s:\{\x7c(?=.{0,700}\x7c\})(?=(?:[^\n]*\n){1,10})[^{}]{0,700}\x7c\})|(?m:^\s*\x7c\s*(?:Path|Key|Source|Item|Name)\s*\x7c[^\n]*\x7c\s*\n\s*\x7c(?:\s*:?-{3,}:?\s*\x7c){2,5}\s*\n(?!(?:\s*\x7c[^\n]*\x7c\s*\n?){7})(?:\s*\x7c[^\n]*(?:\[[^\]]+\]\([^)]+\)|https?://)[^\n]*\x7c\s*\n?){1,6}) low 0.75 Table, Markup, Wikitext wikipedia_signs_ai_writing Very small wikitext table, or small generated-looking Markdown index/link table. Review whether the table is complete and valid. For short link/index lists, use bullets unless a table adds real structure. {| class="wikitable"<br>| Example<br>|}
{|<br>! A<br>| B<br>|}
| Path | Description |<br>| --- | --- |<br>| [samples/x](samples/x) | Example sample set. |
skip_heading_levels (?m)^\s*={3,}\s*[^=\n][^\n]*={3,}\s*$ low 0.5 Headings, Markup, Wikitext wikipedia_signs_ai_writing Potential skipped heading level in wikitext. Check heading hierarchy. === History ===
==== Reception ====
broken_category_syntax \[\[Category:[^\]\|\n]{0,120}[/<>][^\]\n]{0,120}\]\] high 2.0 Category, Markup, Malformed wikipedia_signs_ai_writing Malformed category link with slash or angle-bracket placeholder. Fix or remove malformed category syntax. [[Category:Example/Bad]]
[[Category:<Insert category>]]
attributable_index_json (?:\(attribution\)\{[^}\n]*"attributableIndex"|"attributableIndex"\s*:\s*"?\d+(?:-\d+)?"?) high 2.5 Metadata Leak, API Leak, Citation Leak wikipedia_signs_ai_writing Attribution/citation JSON metadata leaked into text. Strip API metadata and retain only visible citations. (attribution){"attributableIndex":"1-2"}
"attributableIndex": "7"
invalid_reference_reuse (?is)<ref\b[^>]*>[^<]{0,400}<ref\b|</ref>\s*</ref> medium 1.5 Markup, Citation Leak, Malformed wikipedia_signs_ai_writing Nested reference tag or duplicate closing reference tag. Fix the reference markup before publication. <ref>first <ref>nested</ref>
citation</ref></ref>
book_citation_missing_page (?is)\{\{\s*cite\s+book\b(?=[^}]{0,600}\|\s*title\s*=)(?![^}]{0,600}\|\s*pages?\s*=)[^}]{0,600}\}\} none 0.25 No Concern, Citation Quality, Book Citation wikipedia_signs_ai_writing Book citation without page/pages field. Citation-quality signal, not AI evidence. Only flag in a future citation-quality/no-concern mode. {{cite book |title=Example book |author=A. Writer}}
{{ cite book | title=Long book | publisher=Press }}
edit_summary_first_person (?i)\b(?:I\s+(?:revised|updated|improved|adjusted|expanded|refined|corrected|added|removed|ensured|avoided)\b|my\s+(?:edit|changes|summary)\b) low 0.75 Edit Summary, First Person wikipedia_signs_ai_writing First-person edit-summary language in copied text. Check whether an edit summary or chatbot response was pasted into content. I revised the article for neutrality.
My edit improves the sourcing.
afc_submission_statement (?i)(?:\bReviewer\s+note\s*\(for\s+AfC\)|\bThis\s+draft\s+is\s+a\s+neutral\s+and\s+well[-\s]sourced\s+biography\b|\bAfC\s+submission\b) high 2.0 AfC, Template, Markup wikipedia_signs_ai_writing AfC-specific generated/review artefact. Remove review boilerplate from article prose. Reviewer note (for AfC): this draft has been improved.
This draft is a neutral and well-sourced biography.
wikilawyering_multiple_shortcuts (?im)^(?=(?:.*\bWP:[A-Z0-9]{1,}\b){3,}).{0,400}$ medium 1.25 Talk Page, Policy wikipedia_signs_ai_writing Talk-page line with multiple policy shortcuts. Useful but not necessarily AI. Review whether the policy references are meaningful and relevant. WP:NPOV WP:V WP:OR all apply here.
This violates WP:RS, WP:BLP and WP:UNDUE.

Emoji, legacy, incomplete-copy and notability heuristics

Lower-confidence formatting, notability, summary and incomplete-copy rules.

Rule Regex Severity Weight Tags Sources Description Suggestion Examples
emoji_bulletpoint (?m)^\s*[\U0001F300-\U0001FAFF\u2600-\u27BF]\s+ low 0.75 Emoji, Emoji Bullet, Formatting wikipedia_signs_ai_writing Emoji used as a bullet marker. Use plain list markup if this is formal text. \u2705 Completed the rewrite.
\u1F539 Added source details.
emoji_formatting_inline (?m)(?<!^\s)[\U0001F300-\U0001FAFF\u2600-\u27BF] low 0.5 Emoji, Formatting wikipedia_signs_ai_writing Emoji used as inline formatting or emphasis. Remove decorative emoji from formal content unless intentional. This looks polished \u2705
Please review the section \u1F50D.
didactic_disclaimer (?i)\b(?:it\s+is\s+(?:important|worth|essential)\s+to\s+note\s+that|it\s+should\s+be\s+noted\s+that)\b medium 1.0 LLM Style, Filler wikipedia_signs_ai_writing Didactic note-signposting phrase split from generic filler. Remove the phrase and state the point directly. It is important to note that costs vary.
It should be noted that the source is incomplete.
section_summary_transition (?i)\b(?:in\s+summary|to\s+summari[sz]e|overall)\b medium 1.0 Essay Style, Transition wikipedia_signs_ai_writing Section-summary transition split from conclusion-transition phrasing. Use a specific summary point or omit the transition. In summary, the change worked.
Overall, the release was successful.
ai_prompt_refusal (?i)\b(?:as\s+an\s+AI\s+language\s+model|I\s+cannot\s+(?:assist|help)\s+with|I\s+can\s+not\s+(?:assist|help)\s+with|I\s+am\s+unable\s+to\s+comply)\b critical 3.0 Chatbot Artefact, Refusal, Prompt Leak wikipedia_signs_ai_writing Raw chatbot refusal or model-identity phrase. Remove prompt-response artefacts from final text. As an AI language model, I cannot assist with that.
I am unable to comply with this request.
abrupt_cutoff (?m)\b[A-Za-z]{3,}(?:\.{2,}|\u2026)\s*$ low 0.5 Incomplete wikipedia_signs_ai_writing Abrupt trailing ellipsis or incomplete copy at end of line. Check whether text was truncated during copy/paste or generation. The article continues with additional...
The sentence trails off\u2026
significance_legacy_trend (?i)\b(?:plays\s+a\s+(?:significant|pivotal|crucial)\s+role|represents\s+a\s+(?:key|significant|major|pivotal)\s+(?:shift|moment)|marks\s+a\s+(?:turning\s+point|milestone)|serves\s+as\s+a\s+(?:catalyst|focal\s+point)|drives\s+the\s+(?:growth|evolution)\s+of|mirrors\s+(?:broader|wider)\s+trends|embodies\s+the\s+(?:ongoing|contemporary)\s+discourse|underscores\s+the\s+(?:importance|legacy|relevance))\b medium 1.75 Marketing Style, Legacy Hype, Low Specificity wikipedia_signs_ai_writing Phrase-level significance/legacy/trend hype split out from vocabulary and promotional rules. Replace broad significance claims with concrete sourced facts. The album plays a significant role in the scene.
The release mirrors broader trends in digital media.
canned_notability_media (?i)\b(?:independent\s+(?:local|regional|national|international)\s+(?:media|news|press)\s+(?:outlets|coverage)|profiled\s+in\s+(?:[^,\n]+,\s*){1,3}(?:and\s+)?[^,\n]+|featured\s+in\s+(?:[^,\n]+,\s*){1,3}(?:and\s+)?[^,\n]+|(?:active|strong)\s+(?:social\s+media|online)\s+presence|on\s+(?:platforms|social\s+media)\s+such\s+as\s+[^.\n]{3,80})\b medium 1.25 Notability, Evidence Gap, Marketing Style wikipedia_signs_ai_writing Canned notability/media-coverage argument pattern. Name specific reliable independent sources and avoid generic notability claims. The subject has independent local media coverage.
She was profiled in Example News, Local Times, and Arts Weekly.
exhaustive_edit_summary (?i)\bI\s+(?:revised|updated|improved|ensured|expanded|removed)\s+[^\n]{50,180}\b(?:WP:[A-Za-z]{2,}|Neutral\s+Point\s+of\s+View|Verifiability|Original\s+Research|NPOV|reliable\s+sources)\b medium 1.5 Edit Summary, First Person, Policy wikipedia_signs_ai_writing Verbose first-person edit summary with policy-heavy justification. Keep edit summaries out of article prose. I revised the lead, improved sourcing, removed unsourced claims, and ensured compliance with WP:NPOV.
I updated the draft to improve sourcing, remove unsupported claims, clarify the lead, and improve Verifiability.
hallucinated_category_name \[\[Category:[^\]\n]{30,120}\]\] low 0.5 Category, Hallucination wikipedia_signs_ai_writing Improbably long category name. Weak signal; can be legitimate. Verify that the category exists and is appropriate. [[Category:Contemporary interdisciplinary artists from the upper valley region]]
[[Category:People associated with regional innovation and digital outreach]]

Samples and corpus

Included sample sets

This covers the sample material in the repository, including the public sample corpus and the smaller test fixture sets.

  • samples/wikipedia_signs_ai_writing/ - Bundled Wikipedia "Signs of AI writing" example corpus used for detector tuning and scorer checks. Contains 198 .txt samples: 175 quote examples and 23 preformatted examples.
  • tests/samples/ - Small text fixtures used by tests and manual checks. Contains 6 direct .txt files, including short/long AI-style burger essays, a medium article sample, OpenAI Harmony page text and quick smoke-test text.
  • tests/samples/ai_filetype_detection_samples/ - File-type extraction fixture set used by tests/test_score_samples.py. Contains 36 files covering plain text, code, CSV/TSV, JSON/JSONL, YAML, TOML, INI, properties, log, Markdown, LaTeX, HTML/XML, EML, EPUB, XLSX, DOCX, ODT, RTF, PDF, image, binary and SQLite cases.

The Wikipedia corpus filenames include the index, block type and source section slug, for example:

001_quote_undue_emphasis_on_significance_legacy_and_broader_trends.txt
066_pre_not_x_but_y.txt

The file-type fixture names are deliberately literal, for example:

sample_markdown_fenced_block.md
sample_word_document.docx
sample_searchable_text_pdf.pdf

Generated score reports

score_samples.py writes these files by default under results/ or another folder passed with --out:

  • file_scores.csv - one row per scored file, including source kind, match counts and scores.
  • rules_by_file.csv - one row per matched rule per file.
  • rule_frequency.csv - aggregate rule frequency across the scanned files.
  • skipped_files.csv - files ignored or failed during extraction.
  • file_scores.json - full structured output.

The scorer imports local glyphguard.py headlessly and uses the same rule loader, text extractor and score-breakdown methods as the app.

Adding new samples

Add new corpora under samples/<corpus_name>/ as plain .txt files. Keep one source block per file where possible. Name files with an index, block type and short source slug so sorting is stable and the source is still recognisable.

If the corpus was generated from a page or document, include a manifest with source path, block kind, character count and word count so future scoring runs are traceable. Then run the scorer and review the generated CSV/JSON output before committing sample changes.

Example:

python score_samples.py samples/<corpus_name> --out results

Testing

GlyphGuard has unit tests for the app/rule behaviours, a one-test-per-rule suite for holistic rules, and scorer tests.

Run tests

Run the full test suite:

python -m pytest -q

Run the main app/rule behaviour tests:

python -m pytest -q tests/test_glyphguard.py

Run the one-test-per-rule suite:

python -m pytest -q tests/test_holistic_rules.py

Run scorer tests:

python -m pytest -q tests/test_score_samples.py

Rule test structure

tests/test_holistic_rules.py tests every production pattern explicitly. It includes flattened examples, extra positive and negative cases, metadata checks and overlap guards where similar rules could double-count the same text.

Sample smoke tests

python score_samples.py samples/wikipedia_signs_ai_writing --out /tmp/glyphguard_score_smoke
python score_samples.py samples/wikipedia_signs_ai_writing --limit 10 --out /tmp/glyphguard_score_smoke_limited

Project structure

This repository contains the following files and folders:

Configuration

Preferences are saved to glyphguard_settings.json. The app first tries to write that file beside glyphguard.py; if that is not writable it falls back to ~/.glyphguard_settings.json.

Saved settings include:

  • per-character detection toggles;
  • replacement toggles and replacement overrides;
  • detection-table visibility and category filters;
  • panel collapsed/expanded state and remembered widths.

Delete the settings file to reset preferences.

The main rule data lives in these constants:

  • TRACKED_CHAR_SPECS - glyph-level detections and replacement defaults.
  • HOLISTIC_AI_PATTERN_SPECS - holistic writing-pattern and tool-artefact rules.
  • HOLISTIC_SOURCE_CATALOG - source metadata used by holistic rules.
  • HOLISTIC_TAG_COLOURS - colour mapping for semantic tags.

Reports and output files

The UI can export:

  • PDF reports with summary scores, charts, glyph rows, holistic rule rows, original text and cleaned text.
  • Plain-text reports with the same review information in a simpler form.

The scorer writes CSV and JSON output under results/ by default, or the folder passed with --out.

Limitations

  • Results are heuristic and can be wrong.
  • The PDF extractor is small and stdlib-only. It works best on text-based PDFs and does not do OCR.
  • DOCX, ODT and RTF extraction is best-effort visible-text extraction, not a full document renderer.
  • It does not inspect images, screenshots, white-on-white text, CSS tricks or layout-level hiding.
  • Some legitimate writing will hit AI-style rules. That is expected.
  • A clean scan does not prove a document is safe, human-written or free from hidden intent.

Ideas/Roadmap

Potential improvements:

  • American/British/International English/Multilingual modes
    • american mode picks up british stuff
    • British/International English modes pick up american spelling and stuff
    • British takes Welsh letters as No concern
    • European discounts all European native languge characters (broad).
    • Multilingual ignores all region based characters as No concern.
  • rework report completely, utter garbage atm
  • cleaner command-line output for batch scoring
  • better generated screenshot flow
  • richer sample corpus metadata
    • need actual docs of different types with real examples
    • need to find a good source of tagged AI stuff
    • need to find a good detector of AI slop
  • Webpage detector/scorer - Help avoid AI slop webpages quickly
    • browser plugin, colud do taperscript?
    • wonder how many pages have hidden chars (wonder if its a SEO way to get more traffic from LLMS lol)
  • stronger extraction for awkward PDFs and document formats
  • more file support
  • AI code detection speific rules/mode (gotta look up research papers about this)
  • more model/vendor specific detections.
  • model/vendor guess when evidence is statistically strong enough.
  • optional packaging or a requirements file

Contributing

Contributions are welcome. Try to keep the app local and practical.

Holistic rules need to have sources or strong evidence of some kind, please reference your sources!

Please include tests and documentation updates when changing user-visible behaviour, adding rules etc.

Avoid uncommon heavyweight dependencies unless they are clearly justified.

Adding a new glyph rule

Add an entry to TRACKED_CHAR_SPECS with the code point, visibility, description, example, replacement behaviour, keyboard equivalent, HTML/raw value and any kind or concern override. Then add or update tests in tests/test_glyphguard.py for detection, concern level and cleaning behaviour.

If the character is not explicitly tracked, check whether the derived Unicode metadata path already handles it. Add a curated entry when the character has a specific replacement, source, concern level or explanation that the fallback metadata cannot know.

Adding a new holistic rule

Add a rule to HOLISTIC_AI_PATTERN_SPECS. Include:

  • name
  • regex
  • weight
  • severity
  • examples
  • description
  • suggestion
  • sources
  • tags

Use a stable snake_case name. Keep the regex narrow enough to explain. Add examples that show the intended match, and add negative tests for wording that should not match. If you use a new source key, add it to HOLISTIC_SOURCE_CATALOG. If you use a new tag, add it to HOLISTIC_TAG_COLOURS. Add an explicit test in tests/test_holistic_rules.py, including overlap guards where nearby rules might also match.

Adding or updating samples

Keep one source block per .txt file. Use clear filenames with an index, block type and source slug. If possible, add a manifest. Then run:

python score_samples.py samples/<corpus_name> --out results

Review file_scores.csv, rules_by_file.csv, rule_frequency.csv and file_scores.json before committing. If a rule change deliberately shifts scores across the corpus, mention that in the changelog.

Updating tests

Update tests when changing glyph rules, holistic rules, sample extraction, score output or overlap behaviour. Rule changes should include positive examples, negative examples and overlap guards where nearby rules might also match.

Sources and references

These references are used as provenance for review heuristics, background or related materials.

Sources/references for holistic patterns

Background/Useful

  • Promptfoo: Hidden Unicode attacks - useful background on hidden Unicode and invisible prompt-injection characters. This is background for the README rather than a current HOLISTIC_SOURCE_CATALOG entry.

Changelog

See CHANGELOG.md for release notes. Record user-visible detection changes, corpus updates, scoring changes and README/test structure changes there.

License

This project is distributed under the MIT License. See LICENSE for details.

About

GlyphGuard provides a Tkinter interface for inpecting documents, pasting text, inspecting suspicious unicode and AI artefacts in real time, tune what gets flagged, and export the full report when needed.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors