@opacedev/astro-ai-content-checker 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/LICENSE +9 -0
  3. package/README.md +236 -0
  4. package/SBOM.cdx.json +172 -0
  5. package/SECURITY.md +9 -0
  6. package/THIRD_PARTY_NOTICES.md +36 -0
  7. package/dist/build-report-html.d.ts +3 -0
  8. package/dist/highlight.js +155 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.js +43 -0
  11. package/dist/options.d.ts +21 -0
  12. package/dist/options.js +6 -0
  13. package/dist/receipt.d.ts +15 -0
  14. package/dist/receipt.js +100 -0
  15. package/dist/report.d.ts +55 -0
  16. package/dist/report.js +12 -0
  17. package/dist/sections.js +181 -0
  18. package/dist/share.d.ts +47 -0
  19. package/dist/share.js +72 -0
  20. package/dist/shared-EX54KI6H.js +1503 -0
  21. package/dist/shared-MMXWTX2U.js +43 -0
  22. package/dist/toolbar.js +13478 -0
  23. package/node_modules/@opacedev/ai-content-checker-browser/LICENSE +21 -0
  24. package/node_modules/@opacedev/ai-content-checker-browser/README.md +145 -0
  25. package/node_modules/@opacedev/ai-content-checker-browser/dist/dom/visible-text.d.ts +14 -0
  26. package/node_modules/@opacedev/ai-content-checker-browser/dist/dom/visible-text.js +47 -0
  27. package/node_modules/@opacedev/ai-content-checker-browser/dist/index.d.ts +3 -0
  28. package/node_modules/@opacedev/ai-content-checker-browser/dist/index.js +99 -0
  29. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/client.d.ts +12 -0
  30. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/client.js +17 -0
  31. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/entry.d.ts +1 -0
  32. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/entry.js +3984 -0
  33. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/protocol.d.ts +30 -0
  34. package/node_modules/@opacedev/ai-content-checker-browser/dist/worker/protocol.js +1 -0
  35. package/node_modules/@opacedev/ai-content-checker-browser/package.json +60 -0
  36. package/node_modules/@opacedev/ai-content-checker-contracts/LICENSE +21 -0
  37. package/node_modules/@opacedev/ai-content-checker-contracts/README.md +93 -0
  38. package/node_modules/@opacedev/ai-content-checker-contracts/dist/index.d.ts +58 -0
  39. package/node_modules/@opacedev/ai-content-checker-contracts/dist/index.js +17 -0
  40. package/node_modules/@opacedev/ai-content-checker-contracts/package.json +57 -0
  41. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/analysis-request.schema.d.ts +44 -0
  42. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/analysis-result.schema.d.ts +122 -0
  43. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/candidate.schema.d.ts +81 -0
  44. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/capabilities.schema.d.ts +24 -0
  45. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/checker-result.schema.d.ts +342 -0
  46. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/common.schema.d.ts +10 -0
  47. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/envelope.schema.d.ts +14 -0
  48. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/gate-result.schema.d.ts +19 -0
  49. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/integrity-receipt.schema.d.ts +122 -0
  50. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/job.schema.d.ts +188 -0
  51. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/method-result.schema.d.ts +39 -0
  52. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/pattern-finding.schema.d.ts +26 -0
  53. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/protected-span.schema.d.ts +35 -0
  54. package/node_modules/@opacedev/ai-content-checker-contracts/src/generated/rewrite-request.schema.d.ts +65 -0
  55. package/node_modules/@opacedev/ai-content-checker-contracts/src/index.ts +63 -0
  56. package/node_modules/@opacedev/ai-content-checker-contracts/src/semantic-validation.mjs +32 -0
  57. package/node_modules/@opacedev/ai-content-checker-core/LICENSE +21 -0
  58. package/node_modules/@opacedev/ai-content-checker-core/README.md +186 -0
  59. package/node_modules/@opacedev/ai-content-checker-core/dist/bundle.js +4744 -0
  60. package/node_modules/@opacedev/ai-content-checker-core/dist/capabilities.d.ts +16 -0
  61. package/node_modules/@opacedev/ai-content-checker-core/dist/capabilities.js +7 -0
  62. package/node_modules/@opacedev/ai-content-checker-core/dist/diff/diff.d.ts +17 -0
  63. package/node_modules/@opacedev/ai-content-checker-core/dist/diff/diff.js +76 -0
  64. package/node_modules/@opacedev/ai-content-checker-core/dist/fixes/preview.d.ts +27 -0
  65. package/node_modules/@opacedev/ai-content-checker-core/dist/fixes/preview.js +47 -0
  66. package/node_modules/@opacedev/ai-content-checker-core/dist/gates/policy.d.ts +14 -0
  67. package/node_modules/@opacedev/ai-content-checker-core/dist/gates/policy.js +16 -0
  68. package/node_modules/@opacedev/ai-content-checker-core/dist/index.d.ts +15 -0
  69. package/node_modules/@opacedev/ai-content-checker-core/dist/index.js +15 -0
  70. package/node_modules/@opacedev/ai-content-checker-core/dist/inspect.d.ts +8 -0
  71. package/node_modules/@opacedev/ai-content-checker-core/dist/inspect.js +84 -0
  72. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-gb-v1.d.ts +12 -0
  73. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-gb-v1.js +68 -0
  74. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2-data.d.ts +73 -0
  75. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2-data.js +499 -0
  76. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2.d.ts +76 -0
  77. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v2.js +1067 -0
  78. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3-data.d.ts +165 -0
  79. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3-data.js +614 -0
  80. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3.d.ts +19 -0
  81. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v3.js +542 -0
  82. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-corpus.d.ts +40 -0
  83. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-corpus.js +40 -0
  84. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-data.d.ts +138 -0
  85. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4-data.js +200 -0
  86. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4.d.ts +71 -0
  87. package/node_modules/@opacedev/ai-content-checker-core/dist/patterns/en-signals-v4.js +388 -0
  88. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/extract.d.ts +17 -0
  89. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/extract.js +75 -0
  90. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/validate.d.ts +3 -0
  91. package/node_modules/@opacedev/ai-content-checker-core/dist/protected/validate.js +17 -0
  92. package/node_modules/@opacedev/ai-content-checker-core/dist/provenance/c2pa-text.d.ts +55 -0
  93. package/node_modules/@opacedev/ai-content-checker-core/dist/provenance/c2pa-text.js +86 -0
  94. package/node_modules/@opacedev/ai-content-checker-core/dist/receipts/build.d.ts +48 -0
  95. package/node_modules/@opacedev/ai-content-checker-core/dist/receipts/build.js +61 -0
  96. package/node_modules/@opacedev/ai-content-checker-core/dist/report/checker-result.d.ts +241 -0
  97. package/node_modules/@opacedev/ai-content-checker-core/dist/report/checker-result.js +419 -0
  98. package/node_modules/@opacedev/ai-content-checker-core/dist/source/offsets.d.ts +34 -0
  99. package/node_modules/@opacedev/ai-content-checker-core/dist/source/offsets.js +51 -0
  100. package/node_modules/@opacedev/ai-content-checker-core/dist/source/utf8.d.ts +3 -0
  101. package/node_modules/@opacedev/ai-content-checker-core/dist/source/utf8.js +58 -0
  102. package/node_modules/@opacedev/ai-content-checker-core/dist/source/visible-text.d.ts +12 -0
  103. package/node_modules/@opacedev/ai-content-checker-core/dist/source/visible-text.js +30 -0
  104. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/data.d.ts +22 -0
  105. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/data.js +137 -0
  106. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/inspect.d.ts +16 -0
  107. package/node_modules/@opacedev/ai-content-checker-core/dist/unicode/inspect.js +84 -0
  108. package/node_modules/@opacedev/ai-content-checker-core/dist/verdict/combine.d.ts +162 -0
  109. package/node_modules/@opacedev/ai-content-checker-core/dist/verdict/combine.js +501 -0
  110. package/node_modules/@opacedev/ai-content-checker-core/package.json +62 -0
  111. package/node_modules/canonicalize/LICENSE +201 -0
  112. package/node_modules/canonicalize/README.md +65 -0
  113. package/node_modules/canonicalize/bin/canonicalize.js +17 -0
  114. package/node_modules/canonicalize/lib/canonicalize.d.ts +2 -0
  115. package/node_modules/canonicalize/lib/canonicalize.js +74 -0
  116. package/node_modules/canonicalize/package.json +64 -0
  117. package/node_modules/entities/LICENSE +11 -0
  118. package/node_modules/entities/lib/decode.d.ts +211 -0
  119. package/node_modules/entities/lib/decode.d.ts.map +1 -0
  120. package/node_modules/entities/lib/decode.js +536 -0
  121. package/node_modules/entities/lib/decode.js.map +1 -0
  122. package/node_modules/entities/lib/decode_codepoint.d.ts +19 -0
  123. package/node_modules/entities/lib/decode_codepoint.d.ts.map +1 -0
  124. package/node_modules/entities/lib/decode_codepoint.js +76 -0
  125. package/node_modules/entities/lib/decode_codepoint.js.map +1 -0
  126. package/node_modules/entities/lib/encode.d.ts +22 -0
  127. package/node_modules/entities/lib/encode.d.ts.map +1 -0
  128. package/node_modules/entities/lib/encode.js +77 -0
  129. package/node_modules/entities/lib/encode.js.map +1 -0
  130. package/node_modules/entities/lib/escape.d.ts +43 -0
  131. package/node_modules/entities/lib/escape.d.ts.map +1 -0
  132. package/node_modules/entities/lib/escape.js +122 -0
  133. package/node_modules/entities/lib/escape.js.map +1 -0
  134. package/node_modules/entities/lib/esm/decode.d.ts +211 -0
  135. package/node_modules/entities/lib/esm/decode.d.ts.map +1 -0
  136. package/node_modules/entities/lib/esm/decode.js +496 -0
  137. package/node_modules/entities/lib/esm/decode.js.map +1 -0
  138. package/node_modules/entities/lib/esm/decode_codepoint.d.ts +19 -0
  139. package/node_modules/entities/lib/esm/decode_codepoint.d.ts.map +1 -0
  140. package/node_modules/entities/lib/esm/decode_codepoint.js +71 -0
  141. package/node_modules/entities/lib/esm/decode_codepoint.js.map +1 -0
  142. package/node_modules/entities/lib/esm/encode.d.ts +22 -0
  143. package/node_modules/entities/lib/esm/encode.d.ts.map +1 -0
  144. package/node_modules/entities/lib/esm/encode.js +69 -0
  145. package/node_modules/entities/lib/esm/encode.js.map +1 -0
  146. package/node_modules/entities/lib/esm/escape.d.ts +43 -0
  147. package/node_modules/entities/lib/esm/escape.d.ts.map +1 -0
  148. package/node_modules/entities/lib/esm/escape.js +116 -0
  149. package/node_modules/entities/lib/esm/escape.js.map +1 -0
  150. package/node_modules/entities/lib/esm/generated/decode-data-html.d.ts +3 -0
  151. package/node_modules/entities/lib/esm/generated/decode-data-html.d.ts.map +1 -0
  152. package/node_modules/entities/lib/esm/generated/decode-data-html.js +7 -0
  153. package/node_modules/entities/lib/esm/generated/decode-data-html.js.map +1 -0
  154. package/node_modules/entities/lib/esm/generated/decode-data-xml.d.ts +3 -0
  155. package/node_modules/entities/lib/esm/generated/decode-data-xml.d.ts.map +1 -0
  156. package/node_modules/entities/lib/esm/generated/decode-data-xml.js +7 -0
  157. package/node_modules/entities/lib/esm/generated/decode-data-xml.js.map +1 -0
  158. package/node_modules/entities/lib/esm/generated/encode-html.d.ts +8 -0
  159. package/node_modules/entities/lib/esm/generated/encode-html.d.ts.map +1 -0
  160. package/node_modules/entities/lib/esm/generated/encode-html.js +10 -0
  161. package/node_modules/entities/lib/esm/generated/encode-html.js.map +1 -0
  162. package/node_modules/entities/lib/esm/index.d.ts +96 -0
  163. package/node_modules/entities/lib/esm/index.d.ts.map +1 -0
  164. package/node_modules/entities/lib/esm/index.js +99 -0
  165. package/node_modules/entities/lib/esm/index.js.map +1 -0
  166. package/node_modules/entities/lib/esm/package.json +1 -0
  167. package/node_modules/entities/lib/generated/decode-data-html.d.ts +3 -0
  168. package/node_modules/entities/lib/generated/decode-data-html.d.ts.map +1 -0
  169. package/node_modules/entities/lib/generated/decode-data-html.js +9 -0
  170. package/node_modules/entities/lib/generated/decode-data-html.js.map +1 -0
  171. package/node_modules/entities/lib/generated/decode-data-xml.d.ts +3 -0
  172. package/node_modules/entities/lib/generated/decode-data-xml.d.ts.map +1 -0
  173. package/node_modules/entities/lib/generated/decode-data-xml.js +9 -0
  174. package/node_modules/entities/lib/generated/decode-data-xml.js.map +1 -0
  175. package/node_modules/entities/lib/generated/encode-html.d.ts +8 -0
  176. package/node_modules/entities/lib/generated/encode-html.d.ts.map +1 -0
  177. package/node_modules/entities/lib/generated/encode-html.js +12 -0
  178. package/node_modules/entities/lib/generated/encode-html.js.map +1 -0
  179. package/node_modules/entities/lib/index.d.ts +96 -0
  180. package/node_modules/entities/lib/index.d.ts.map +1 -0
  181. package/node_modules/entities/lib/index.js +126 -0
  182. package/node_modules/entities/lib/index.js.map +1 -0
  183. package/node_modules/entities/package.json +90 -0
  184. package/node_modules/entities/readme.md +122 -0
  185. package/package.json +91 -0
@@ -0,0 +1,499 @@
1
+ /**
2
+ * Rule data for the en-signals v2 editorial-signals engine.
3
+ *
4
+ * Adapted to TypeScript from the MIT-licensed `avoid-ai-writing` detector
5
+ * (`detector/patterns.js`, Conor Bronsdon and contributors) recorded in
6
+ * `source-snapshots/avoid-ai-writing/`. See THIRD_PARTY_NOTICES.md.
7
+ *
8
+ * Everything in this module is a documented writing-pattern rule or a
9
+ * stylometric threshold. All of it is Tier B evidence in BRIEF.md §21 terms:
10
+ * editorial hints about style, never proof of authorship.
11
+ */
12
+ // ─── Homoglyph lookalikes for the normalisation pre-pass ─────────────
13
+ export const CYRILLIC_LOOKALIKES = {
14
+ "а": "a", "е": "e", "о": "o", "р": "p", "с": "c", "х": "x",
15
+ "у": "y", "к": "k", "м": "m", "н": "h", "в": "b", "т": "t",
16
+ "А": "A", "Е": "E", "О": "O", "Р": "P", "С": "C", "Х": "X",
17
+ "У": "Y", "К": "K", "М": "M", "Н": "H", "В": "B", "Т": "T",
18
+ };
19
+ export const GREEK_LOOKALIKES = {
20
+ "ο": "o", "Ο": "O", "α": "a", "Α": "A", "ρ": "p", "Ρ": "P",
21
+ };
22
+ export const ROLEPLAY_VERBS = /^(?:nods|sighs|laughs|smiles|frowns|shrugs|grins|winks|chuckles|gasps|pauses|thinks|wonders|whispers|shouts|gestures|raises|leans|turns|looks|glances|smirks|blinks|nodding|sighing|laughing|smiling|thinking|gesturing)\b/i;
23
+ // ─── Tier 1: always flag ─────────────────────────────────────────────
24
+ export const TIER1 = {
25
+ "delve": "explore, dig into, look at",
26
+ "tapestry": "describe the actual complexity",
27
+ "paradigm": "model, approach, framework",
28
+ "beacon": "rewrite entirely",
29
+ "robust": "strong, reliable, solid",
30
+ "comprehensive": "thorough, complete, full",
31
+ "cutting-edge": "latest, newest, advanced",
32
+ "pivotal": "important, key, critical",
33
+ "meticulous": "careful, detailed, precise",
34
+ "meticulously": "carefully, precisely",
35
+ "seamless": "smooth, easy, without friction",
36
+ "seamlessly": "smoothly, easily",
37
+ "game-changer": "describe what changed",
38
+ "game-changing": "describe what changed",
39
+ "nestled": "is located, sits",
40
+ "vibrant": "describe what makes it active",
41
+ "thriving": "growing, active",
42
+ "bustling": "busy, active",
43
+ "intricate": "complex, detailed",
44
+ "intricacies": "complexities, details",
45
+ "ever-evolving": "changing, growing",
46
+ "enduring": "lasting, long-running",
47
+ "daunting": "hard, difficult",
48
+ "holistic": "complete, full, whole",
49
+ "holistically": "completely, fully",
50
+ "actionable": "practical, useful, concrete",
51
+ "impactful": "effective, significant",
52
+ "learnings": "lessons, findings, takeaways",
53
+ "synergy": "describe the combined effect",
54
+ "synergies": "describe the combined effect",
55
+ "interplay": "relationship, connection",
56
+ "symphony": "describe the coordination",
57
+ "embrace": "adopt, accept, use",
58
+ // 2026.08.3 owner-docs tier A additions (OWNER-DOCS-TELLS.md §8).
59
+ "enigma": "mystery, puzzle",
60
+ "labyrinth": "maze, tangle",
61
+ "top-notch": "excellent, first-rate",
62
+ };
63
+ export const TIER1_PHRASES = [
64
+ { pattern: /\bdelve\s+into\b/gi, replace: "explore, dig into" },
65
+ { pattern: /\blandscape\b/gi, replace: "field, space, industry" },
66
+ { pattern: /\brealm\b/gi, replace: "area, field, domain" },
67
+ { pattern: /\btestament\s+to\b/gi, replace: "shows, proves" },
68
+ { pattern: /\bleverag(?:e|es|ing|ed)\b/gi, replace: "use" },
69
+ { pattern: /\bwatershed\s+moment\b/gi, replace: "turning point, shift" },
70
+ { pattern: /\bmarking\s+a\s+pivotal\s+moment\b/gi, replace: "state what happened" },
71
+ { pattern: /\bthe\s+future\s+looks\s+bright\b/gi, replace: "cut or say something specific" },
72
+ { pattern: /\bonly\s+time\s+will\s+tell\b/gi, replace: "cut or say something specific" },
73
+ { pattern: /\bdespite\s+challenges[^.]*continues?\s+to\s+thrive\b/gi, replace: "name the challenge and response" },
74
+ { pattern: /\bdeep\s+dive\b/gi, replace: "look at, examine" },
75
+ { pattern: /\bdive\s+into\b/gi, replace: "look at, examine" },
76
+ { pattern: /\bunpack(?:ing)?\b/gi, replace: "explain, break down" },
77
+ { pattern: /\bcomplexities\b/gi, replace: "name the actual problems" },
78
+ { pattern: /\bthought\s+leader(?:ship)?\b/gi, replace: "expert, authority" },
79
+ { pattern: /\bbest\s+practices\b/gi, replace: "what works, proven methods" },
80
+ { pattern: /\bat\s+its\s+core\b/gi, replace: "cut, just state it" },
81
+ { pattern: /\bin\s+order\s+to\b/gi, replace: "to", clarity: true },
82
+ { pattern: /\bdue\s+to\s+the\s+fact\s+that\b/gi, replace: "because", clarity: true },
83
+ { pattern: /\bserves\s+as\b/gi, replace: "is", clarity: true },
84
+ { pattern: /\bfeatures\b/gi, replace: "has, includes", clarity: true },
85
+ { pattern: /\bboasts\b/gi, replace: "has", clarity: true },
86
+ { pattern: /\butiliz(?:e|es|ing|ed)\b/gi, replace: "use", clarity: true },
87
+ { pattern: /\bshowcas(?:e|es|ing|ed)\b/gi, replace: "show, demonstrate" },
88
+ { pattern: /\bembark(?:s|ing|ed)?\b/gi, replace: "start, begin" },
89
+ { pattern: /\bcommenc(?:e|es|ing|ed)\b/gi, replace: "start, begin", clarity: true },
90
+ { pattern: /\bascertain(?:s|ing|ed)?\b/gi, replace: "find out, determine", clarity: true },
91
+ { pattern: /\bendeavou?r(?:s|ing|ed)?\b/gi, replace: "effort, attempt, try", clarity: true },
92
+ { pattern: /\bunderscor(?:es|ing|ed)\b/gi, replace: "highlights, shows" },
93
+ // 2026.08.3 owner-docs tier A phrase additions.
94
+ { pattern: /\bbuckle\s+up\b/gi, replace: "cut, or say what actually follows" },
95
+ { pattern: /\bload-bearing\b(?!\s+(?:(?:structural|exterior|interior|internal|external|concrete|steel|timber|wooden|brick|masonry|perimeter|basement|main|primary|existing|original)\s+)?(?:walls?|beams?|columns?|joists?|truss(?:es)?|members?|footings?|slabs?|studs?|partitions?|masonry|lintels?|piers?|rafters?|girders?|capacity|capacities)\b)/gi, replace: "essential, critical, or say what breaks if it is removed" },
96
+ ];
97
+ // ─── Tier 2: flag in clusters (2+ distinct per paragraph) ────────────
98
+ export const TIER2 = {
99
+ "harness": "use, take advantage of",
100
+ "navigate": "work through, handle",
101
+ "navigating": "working through, handling",
102
+ "foster": "encourage, support, build",
103
+ "elevate": "improve, raise, strengthen",
104
+ "unleash": "release, enable, unlock",
105
+ "streamline": "simplify, speed up",
106
+ "empower": "enable, let, allow",
107
+ "bolster": "support, strengthen",
108
+ "spearhead": "lead, drive, run",
109
+ "resonate": "connect with, appeal to",
110
+ "resonates": "connects with, appeals to",
111
+ "revolutionize": "change, transform",
112
+ "facilitate": "enable, help, allow",
113
+ "facilitates": "enables, helps, allows",
114
+ "underpin": "support, form the basis of",
115
+ "nuanced": "specific, subtle, detailed",
116
+ "crucial": "important, key, necessary",
117
+ "multifaceted": "describe the actual facets",
118
+ "ecosystem": "system, community, network",
119
+ "myriad": "many, numerous",
120
+ "plethora": "many, a lot of",
121
+ "encompass": "include, cover, span",
122
+ "catalyze": "start, trigger, accelerate",
123
+ "reimagine": "rethink, redesign, rebuild",
124
+ "galvanize": "motivate, rally, push",
125
+ "augment": "add to, expand, supplement",
126
+ "cultivate": "build, develop, grow",
127
+ "illuminate": "clarify, explain, show",
128
+ "elucidate": "explain, clarify",
129
+ "juxtapose": "compare, contrast",
130
+ "transformative": "describe what changed",
131
+ "transformation": "describe what changed",
132
+ "cornerstone": "foundation, basis, key part",
133
+ "paramount": "most important, top priority",
134
+ "poised": "ready, set, about to",
135
+ "burgeoning": "growing, emerging",
136
+ "nascent": "new, early-stage",
137
+ "quintessential": "typical, classic, defining",
138
+ "overarching": "main, central, broad",
139
+ "quietly": "cut, or name the concrete contrast",
140
+ "underpinning": "basis, foundation",
141
+ "underpinnings": "basis, foundations",
142
+ "paradigm-shifting": "describe what shifted",
143
+ // 2026.08.3 owner-docs additions (medium FP → cluster-scored tier 2).
144
+ "revolutionary": "describe what changed",
145
+ "ai-powered": "say what the AI part actually does",
146
+ };
147
+ export const TIER2_CONDITIONAL = [
148
+ {
149
+ word: "deeply",
150
+ pattern: /\bdeeply\s+(?:integrated|committed|rooted|personal|human|flawed|resonant|transformative|interconnected|ingrained|embedded|meaningful)\b/i,
151
+ suggestion: "cut, or name what specifically runs deep",
152
+ },
153
+ ];
154
+ // ─── Tier 3: flag by density ─────────────────────────────────────────
155
+ export const TIER3 = [
156
+ "significant", "significantly", "innovative", "innovation",
157
+ "effective", "effectively", "dynamic", "dynamics",
158
+ "scalable", "scalability", "compelling", "unprecedented",
159
+ "exceptional", "exceptionally", "remarkable", "remarkably",
160
+ "sophisticated", "instrumental",
161
+ "world-class", "state-of-the-art", "best-in-class",
162
+ "verbatim",
163
+ ];
164
+ export const TIER3_PHRASES = [
165
+ /\bemerging\s+(?:sector|space|category|industry)\b/gi,
166
+ /\bthe\s+integration\s+of\b/gi,
167
+ /\bthe\s+intersection\s+of\b/gi,
168
+ /\bcommunity-?driven\b/gi,
169
+ /\blong-?term\s+sustainability\b/gi,
170
+ /\buser\s+engagement\b/gi,
171
+ /\bdecentralized\s+compute\b/gi,
172
+ /\b(?:sustainable\s+)?reward\s+emissions?\b/gi,
173
+ /\btokenized\s+incentive\s+structures?\b/gi,
174
+ /\bdesigned\s+for\s+long-?term\b/gi,
175
+ ];
176
+ // ─── Weighted phrase categories ──────────────────────────────────────
177
+ export const TRANSITIONS = [
178
+ /\bmoreover\b/gi, /\bfurthermore\b/gi, /\badditionally\b/gi,
179
+ /\bin\s+today'?s\b/gi, /\bin\s+an\s+era\s+where\b/gi,
180
+ /\bit'?s\s+worth\s+noting\s+that\b/gi, /\bnotably\b/gi,
181
+ /\bin\s+conclusion\b/gi, /\bin\s+summary\b/gi, /\bto\s+summarize\b/gi,
182
+ /\bwhen\s+it\s+comes\s+to\b/gi, /\bat\s+the\s+end\s+of\s+the\s+day\b/gi,
183
+ /\bthat\s+(?:being\s+)?said\b/gi,
184
+ // 2026.08.3: ritual-conclusion opener variant (seed str-ritual-conclusion).
185
+ /\bto\s+sum\s+up\b/gi,
186
+ ];
187
+ export const CHATBOT_ARTIFACTS = [
188
+ /\bi\s+hope\s+this\s+helps\b/gi, /\bcertainly!\B/gi, /\babsolutely!\B/gi,
189
+ /\bgreat\s+question!\B/gi, /\bexcellent\s+point!\B/gi,
190
+ /\bfeel\s+free\s+to\s+reach\s+out\b/gi,
191
+ /\blet\s+me\s+know\s+if\s+you\s+need\s+anything\b/gi,
192
+ /\bin\s+this\s+article,?\s+we\s+will\s+explore\b/gi,
193
+ /\blet'?s\s+dive\s+in!?\b/gi,
194
+ // 2026.08.3: chat-wrapper leakage variants (seed art-collab-leakage /
195
+ // art-sycophantic-openers).
196
+ /\bwould\s+you\s+like\s+me\s+to\b/gi,
197
+ /\bi'?d\s+be\s+happy\s+to\b/gi,
198
+ ];
199
+ export const SYCOPHANTIC = [
200
+ /\byou'?re\s+absolutely\s+right\b/gi,
201
+ /\bthat'?s\s+a\s+really\s+insightful\b/gi,
202
+ /\bthat'?s\s+a\s+great\s+question\b/gi,
203
+ /\bexcellent\s+question\b/gi,
204
+ ];
205
+ export const FILLERS = [
206
+ /\bit\s+is\s+important\s+to\s+note\s+that\b/gi,
207
+ /\bin\s+terms\s+of\b/gi,
208
+ /\bthe\s+reality\s+is\s+that\b/gi,
209
+ /\bit'?s\s+important\s+to\s+note\s+that\b/gi,
210
+ ];
211
+ export const GENERIC_CONCLUSIONS = [
212
+ /\bthe\s+future\s+looks\s+bright\b/gi, /\bonly\s+time\s+will\s+tell\b/gi,
213
+ /\bone\s+thing\s+is\s+certain\b/gi, /\bas\s+we\s+move\s+forward\b/gi,
214
+ ];
215
+ export const LETS_PATTERNS = [
216
+ /\blet'?s\s+explore\b/gi, /\blet'?s\s+take\s+a\s+look\b/gi,
217
+ /\blet'?s\s+break\s+this\s+down\b/gi, /\blet'?s\s+examine\b/gi,
218
+ /\blet'?s\s+(?:consider|discuss|delve|unpack|walk\s+through)\b/gi,
219
+ ];
220
+ export const REASONING_ARTIFACTS = [
221
+ /\blet\s+me\s+think\s+step\s+by\s+step\b/gi, /\bbreaking\s+this\s+down\b/gi,
222
+ /\bto\s+approach\s+this\s+systematically\b/gi,
223
+ /\bhere'?s\s+my\s+thought\s+process\b/gi,
224
+ /\bfirst,?\s+let'?s\s+consider\b/gi,
225
+ /\bworking\s+through\s+this\s+logically\b/gi,
226
+ ];
227
+ export const ACKNOWLEDGMENT_LOOPS = [
228
+ /\byou'?re\s+asking\s+about\b/gi, /\bthe\s+question\s+of\s+whether\b/gi,
229
+ /\bto\s+answer\s+your\s+question\b/gi,
230
+ ];
231
+ export const SIGNIFICANCE_INFLATION = [
232
+ /\bmarking\s+a\s+(?:pivotal|significant|important)\s+moment\b/gi,
233
+ /\ba\s+watershed\s+moment\s+for\b/gi,
234
+ /\bin\s+the\s+evolution\s+of\b/gi,
235
+ /\ba\s+(?:pivotal|defining)\s+moment\s+in\b/gi,
236
+ ];
237
+ export const VAGUE_ATTRIBUTIONS = [
238
+ /\bexperts\s+(?:believe|say|suggest|agree)\b/gi,
239
+ /\bstudies\s+(?:show|suggest|indicate)\b/gi,
240
+ /\bresearch\s+(?:shows|suggests|indicates)\b/gi,
241
+ /\bindustry\s+leaders\s+(?:agree|believe|say)\b/gi,
242
+ // 2026.08.3: weasel-attribution variants (seed phr-weasel-attribution).
243
+ /\b(?:observers?|critics|analysts)\s+(?:argue|suggest|believe|say|have\s+(?:cited|noted|shown))\b/gi,
244
+ /\bindustry\s+reports?\s+(?:suggest|show|indicate)\b/gi,
245
+ /\bit\s+is\s+widely\s+believed\b/gi,
246
+ ];
247
+ export const HOLLOW_INTENSIFIERS = [
248
+ /\bgenuine(?:ly)?\b/gi, /\btruly\b/gi, /\bquite\s+frankly\b/gi,
249
+ /\bto\s+be\s+honest\b/gi, /\blet'?s\s+be\s+clear\b/gi,
250
+ ];
251
+ export const EMOTIONAL_FLATLINE = [
252
+ /\bwhat\s+surprised\s+me\s+most\b/gi, /\bi\s+was\s+fascinated\s+to\b/gi,
253
+ /\bwhat\s+struck\s+me\s+was\b/gi, /\bi\s+was\s+excited\s+to\s+learn\b/gi,
254
+ /\bthe\s+most\s+interesting\s+(?:part|thing|aspect|piece)\b/gi,
255
+ /^\s*interesting\s+(?:part|thing|aspect|piece)(?:\s+of\s+(?:the\s+)?\w+)?\s*:/gim,
256
+ ];
257
+ export const LINGERING_ATTENTION = [
258
+ /\b(?:the|that|this)\s+(?:one\s+)?(?:line|quote|bit|part|idea|point|framing|comment|thing)\s+(?:that\s+)?i\s+keep\s+(?:coming\s+back\s+to|thinking\s+about)\b/gi,
259
+ /\bi\s+can'?t\s+stop\s+thinking\s+about\b/gi,
260
+ /\bstill\s+thinking\s+about\s+(?:this|that)\s+one\b/gi,
261
+ /\b(?:been|be)\s+rattling\s+around\s+(?:in\s+)?my\s+(?:head|brain)\b/gi,
262
+ /\bi'?ve\s+been\s+chewing\s+on\s+(?:this|that)\b/gi,
263
+ ];
264
+ export const NOVELTY_INFLATION = [
265
+ /\bthe\s+failure\s+mode\s+nobody'?s?\s+naming\b/gi,
266
+ /\ba\s+problem\s+nobody\s+talks\s+about\b/gi,
267
+ /\bthe\s+insight\s+everyone'?s?\s+missing\b/gi,
268
+ /\bwhat\s+nobody\s+tells\s+you\b/gi,
269
+ ];
270
+ export const CUTOFF_DISCLAIMERS = [
271
+ /\bas\s+of\s+my\s+last\s+update\b/gi,
272
+ /\bas\s+of\s+my\s+(?:knowledge\s+)?(?:cut-?off|last\s+training)\b/gi,
273
+ /\bi\s+don'?t\s+have\s+access\s+to\s+real-?time\s+(?:data|information)\b/gi,
274
+ /\bbased\s+on\s+available\s+information\b/gi,
275
+ /\bas\s+an?\s+(?:ai|artificial\s+intelligence|large\s+language|ai\s+language)\s+(?:language\s+)?model\b/gi,
276
+ /\bi\s+(?:am|'m)\s+an?\s+(?:ai|artificial\s+intelligence|large\s+language)\s+(?:assistant|model)?\b/gi,
277
+ /\bi\s+cannot\s+(?:provide|give|offer)\s+(?:legal|medical|financial|professional)\s+advice\b/gi,
278
+ /\bmy\s+training\s+data\s+(?:only\s+)?(?:goes\s+up\s+to|extends\s+to|ends\s+(?:in|at))\b/gi,
279
+ // 2026.08.3: RAG-era source-gap variant (seed art-cutoff-disclaimer).
280
+ /\bin\s+the\s+provided\s+search\s+results\b/gi,
281
+ ];
282
+ export const AI_PLACEHOLDERS = [
283
+ /\[(?:Your|Insert|Add|Enter|Describe|Specify|Choose|Pick)[^\]\n]{1,80}\]/gi,
284
+ /\[(?:Recipient|Sender|Topic|Subject|Salutation|Closing|Position|Department|Project Name|Company Name|Date)(?:\s+[^\]\n]{0,60})?\]/gi,
285
+ /\[(?:INSERT|FILL\s+IN|ADD|TODO|TBD|PLACEHOLDER)[^\]\n]{0,80}\]/g,
286
+ /\b(?:19|20)\d{2}-XX-XX\b/g,
287
+ /\bXX\/XX\/(?:19|20)\d{2}\b/g,
288
+ /<!--\s*(?:add|fill\s+in|insert|todo|placeholder)[^>]{0,120}-->/gi,
289
+ ];
290
+ export const AI_CITATION_MARKUP = [
291
+ /\bcite(?:turn|news|search|navigation)\d+(?:search|turn|news|navigation)\d+/gi,
292
+ /contentReference\s*\[oaicite:[^\]]+\]\s*\{[^}]*\}/gi,
293
+ /\boai_citation\b/gi,
294
+ /\[attached_file:\d+\]/gi,
295
+ /\bgrok_card\b/gi,
296
+ ];
297
+ export const AI_UTM_SOURCE = [
298
+ /[?&]utm_source=(?:chatgpt|openai|copilot|claude|grok|gemini|perplexity)(?:\.com|\.ai)?\b/gi,
299
+ /[?&]referrer=(?:chatgpt|copilot|grok|claude|gemini|perplexity)\.(?:com|ai)\b/gi,
300
+ ];
301
+ export const TEMPLATE_PHRASES = [
302
+ /\ba\s+\w+\s+step\s+(?:towards?|forward\s+for)\b/gi,
303
+ /\bwhether\s+you'?re\s+\w+\s+or\s+\w+/gi,
304
+ /\bi\s+recently\s+had\s+the\s+pleasure\s+of\b/gi,
305
+ ];
306
+ export const FALSE_CONCESSION = [
307
+ /\bwhile\s+\w+\s+is\s+impressive\b/gi,
308
+ /\balthough\s+\w+\s+has\s+made\s+strides\b/gi,
309
+ /\bdespite\s+\w+\s+challenges?\b/gi,
310
+ ];
311
+ export const RHETORICAL_QUESTIONS = [
312
+ /\bbut\s+what\s+does\s+this\s+mean\s+for\b/gi,
313
+ /\bso\s+why\s+should\s+you\s+care\b/gi,
314
+ /\bwhat'?s\s+next\?\s*/gi,
315
+ ];
316
+ export const HEDGE_STACK = [
317
+ /\b(?:could|may|might)\s+(?:(?!not\b|never\b|hardly\b|scarcely\b|barely\b)\w+\s+)?(?:potentially|eventually|ultimately|possibly|conceivably)\b/gi,
318
+ /\b(?:potentially|eventually|ultimately)\s+(?:could|may|might)\b/gi,
319
+ ];
320
+ export const FUTURE_NARRATIVE = [
321
+ /\b(?:may|could|will|is\s+(?:poised|set)\s+to)\s+become\s+(?:one\s+of\s+)?(?:the\s+)?(?:most\s+)?\w+\s+(?:narratives?|stories|developments?|trends?|movements?|chapters?|themes?|forces?)\b/gi,
322
+ /\bone\s+of\s+the\s+most\s+important\s+(?:narratives?|stories|trends?|themes?)\s+of\s+the\s+(?:next|coming)\s+\w+\b/gi,
323
+ ];
324
+ export const REAL_ACTUAL_INFLATION = [
325
+ /\b(?:real|actual|genuine|true)\s+(?:on-?chain\s+)?(?:tokenomics|economics|utility|adoption|sustainability|impact|revenue|fundamentals|demand|value|innovation|traction)\b/gi,
326
+ ];
327
+ export const FORMULAIC_OPENERS = [
328
+ /\bin\s+the\s+(?:rapidly\s+|ever-?\s*)?(?:evolving|changing|expanding|growing|shifting)\s+(?:world|landscape|realm|space|field|domain|era)\s+of\b/gi,
329
+ /\bin\s+(?:an?|the)\s+(?:digital\s+)?age\s+(?:where|of)\b/gi,
330
+ /\bas\s+(?:we|the\s+world|society|industries?)\s+(?:continue|move|navigate|enter)\s+(?:to\s+)?(?:evolve|forward|into|through)\b/gi,
331
+ /\bhas\s+emerged\s+as\s+(?:a|the|one\s+of)\s+(?:leading|key|major|critical|essential|fundamental|pivotal|prominent|dominant|important)\s+\w+/gi,
332
+ /\bhas\s+become\s+increasingly\s+(?:important|critical|popular|relevant|prominent|essential)\b/gi,
333
+ ];
334
+ export const SPECULATIVE_OPENERS = [
335
+ /\b(?:imagine|picture|envision)(?:\s*,[^,\n]{1,30},)?\s+a\s+(?:world|future|reality)\s+(?:where|in\s+which)\b/gi,
336
+ ];
337
+ export const PARENTHETICAL_HEDGE = [
338
+ /\(\s*(?:and\s+)?(?:increasingly|notably|importantly|crucially|interestingly|perhaps)[,]?\s+[^)]{3,60}\)/gi,
339
+ /\(\s*or\s+more\s+(?:precisely|accurately|specifically)[,]?\s+[^)]{3,60}\)/gi,
340
+ /\(\s*though\s+to\s+be\s+fair[,]?\s+[^)]{3,60}\)/gi,
341
+ /\(\s*at\s+least\s+(?:in\s+)?(?:theory|principle|part)[,]?\s+[^)]{0,60}\)/gi,
342
+ ];
343
+ export const CONFIDENCE_CALIBRATION = [
344
+ /\binterestingly\b/gi, /\bsurprisingly\b/gi, /\bimportantly\b/gi,
345
+ /\bsignificantly\b/gi, /\bcertainly\b/gi, /\bundoubtedly\b/gi,
346
+ /\bwithout\s+a\s+doubt\b/gi,
347
+ ];
348
+ export const SOCIAL_CTA_CLOSER = [
349
+ /\bthis\s+one['’]?s?\s+(?:is\s+)?(?:well\s+|totally\s+|absolutely\s+|definitely\s+|really\s+|truly\s+|easily\s+|more\s+than\s+)?worth\s+(?:your\s+time|the\s+read|a\s+read|every\s+(?:minute|second)|reading|watching|a\s+listen|a\s+watch|a\s+look|it)\b/gi,
350
+ /\bthis\s+one['’]?s?\s+(?:is\s+)?a\s+must[-\s]?(?:read|watch|listen|see)\b/gi,
351
+ /\b(?:highly|strongly|can['’]?t|cannot)\s+recommend\w*\s+(?:giving\s+)?(?:this|it)\s+(?:one\s+)?a\s+(?:read|listen|watch|look|go)\b/gi,
352
+ /\bdo\s+yourself\s+a\s+favou?r\s+and\s+(?:read|watch|check\s+out)\s+(?:this|it)\b/gi,
353
+ /\byou\s+(?:really\s+)?(?:won['’]?t|do\s*n['’]?t|will\s+not|do\s+not)\s+want\s+to\s+miss\s+this(?:\s+one)?(?=\s*(?:[:.!\n]|$))/gi,
354
+ /(?<=^|[,.!?:\n]\s{0,4})(?:you\s+can\s+)?thank\s+me\s+later\b/gim,
355
+ /(?<=^|[.!?:\n]\s{0,4})save\s+this\s+(?:one\s+)?for\s+later\b/gim,
356
+ /\bbookmark\s+this(?:\s+(?:one|post|thread))?(?=\s*(?:[:.!\n]|$))/gi,
357
+ /\bdo\s*n['’]?t\s+sleep\s+on\s+this\b/gi,
358
+ /\btrust\s+me,?\s+(?:on\s+this|you['’]?ll)\b/gi,
359
+ ];
360
+ // ─── "isn't just X — it's Y" contrast construction ──────────────────
361
+ // Opace addition (not in upstream): the negated-contrast template that pairs
362
+ // "isn't/not just X" with a dash and an "it's Y" punchline.
363
+ export const NOT_JUST_CONTRAST = [
364
+ /\b(?:isn|aren|wasn|weren|doesn|don|didn)['’]t\s+(?:just\s+)?[^.!?\n]{1,60}?\s*(?:—|–|--)\s*(?:it|this|that|they|we)['’](?:s|re)\b/gi,
365
+ /\b(?:is|are|was|were)\s+not\s+just\s+[^.!?\n]{1,60}?\s*(?:—|–|--|,)\s*(?:it|this|that|they)['’](?:s|re)\b/gi,
366
+ /\bnot\s+just\s+[^.!?\n]{1,60}?\s*(?:—|–|--)\s*(?:but\s+)?(?:it|this|that|they)['’](?:s|re)\b/gi,
367
+ ];
368
+ export const FUNCTION_WORD_IN_TITLE = /\b(?:And|Or|Of|The|In|For|To|A|An)\b/;
369
+ export const MD_HEADING_PREFIX = /^#{1,6}[ \t]+/;
370
+ export const TITLE_CASE_HEADER = /^(?:#{1,6}[ \t]+)?([A-Z][a-z]+(?:\s+(?:[A-Z][a-z]+|and|or|of|the|in|for|to|a|an))+\s+[A-Z][a-z]+)\s*$/gm;
371
+ export const SEPARATOR_DASH_RE = /^\s*(?:[-*+]|\d+[.)])\s+(?:\*\*[^*\n]+\*\*|\[[^\]\n]+\]\([^)\n]*\))(?:[ \t]*(?:\([^)\n]*\)|`[^`\n]+`))?[ \t]*—/gm;
372
+ export const VERSION_HEADING_DASH_RE = /^#{1,6}[ \t]+\[?v?\d+\.\d+\.\d+[^\]\n]*\]?[ \t]*—[ \t]*\d{4}-\d{2}-\d{2}[ \t]*$/gm;
373
+ export const FUNC_WORDS = new Set([
374
+ "the", "a", "an", "and", "or", "but", "of", "to", "in", "on", "at", "by", "for", "with",
375
+ "from", "as", "is", "was", "are", "were", "be", "been", "being", "have", "has", "had",
376
+ "do", "does", "did", "will", "would", "should", "could", "may", "might", "must", "can",
377
+ "this", "that", "these", "those", "it", "its", "they", "them", "their", "there", "here",
378
+ "we", "our", "us", "i", "you", "your", "he", "she", "his", "her", "him", "not", "no", "so",
379
+ "if", "then", "than", "when", "where", "which", "who", "what", "how", "why", "because",
380
+ ]);
381
+ // Per-category score weights applied to the deduplicated issue list —
382
+ // values carried over verbatim from the upstream ISSUE_WEIGHTS table.
383
+ export const ISSUE_WEIGHTS = {
384
+ "tier1": 5,
385
+ "tier1-clarity": 3,
386
+ "tier2": 3,
387
+ "tier3": 2,
388
+ "transition": 2,
389
+ "chatbot": 8,
390
+ "sycophantic": 8,
391
+ "filler": 2,
392
+ "generic-conclusion": 3,
393
+ "lets-construction": 2,
394
+ "reasoning-artifact": 6,
395
+ "acknowledgment-loop": 3,
396
+ "significance-inflation": 4,
397
+ "vague-attribution": 5,
398
+ "hollow-intensifier": 2,
399
+ "emotional-flatline": 2,
400
+ "lingering-attention": 3,
401
+ "novelty-inflation": 3,
402
+ "cutoff-disclaimer": 10,
403
+ "template-phrase": 3,
404
+ "false-concession": 2,
405
+ "rhetorical-question": 2,
406
+ "confidence-calibration": 2,
407
+ "em-dash-density": 4,
408
+ "not-just-contrast": 6,
409
+ "uniform-sections": 5,
410
+ "uniform-list-items": 4,
411
+ "sentence-flatline": 5,
412
+ "uniformity": 5,
413
+ "formatting": 3,
414
+ "tier3-phrase": 3,
415
+ "tier3-phrase-cluster": 12,
416
+ "hashtag-stuff": 12,
417
+ "bullet-np-list": 10,
418
+ "hedge-stack": 6,
419
+ "future-narrative": 12,
420
+ "real-actual-inflation": 5,
421
+ "social-cta-closer": 8,
422
+ "formulaic-opener": 8,
423
+ "speculative-opener": 8,
424
+ "title-case-header": 4,
425
+ "parenthetical-hedge": 3,
426
+ "smart-punct-signature": 6,
427
+ "punct-distribution": 6,
428
+ "fnword-trigram-entropy": 5,
429
+ "cross-para-burstiness": 5,
430
+ "normalization-flag": 9,
431
+ "low-ttr": 3,
432
+ "ai-placeholder": 10,
433
+ "ai-citation-markup": 15,
434
+ "ai-utm-source": 12,
435
+ };
436
+ // Plain-language pass, 30 August 2026. Every message names the thing, gives an
437
+ // example where the rule allows one, and the suggestion is an action.
438
+ //
439
+ // The repeated "this is a stylistic hint, not evidence of authorship" tail that
440
+ // used to end all 113 messages is gone on purpose. It was printed 113 times per
441
+ // report, which is how a caveat becomes invisible; the display layer states the
442
+ // claim boundary ONCE per panel instead (design spec
443
+ // internal programme record, maintained privately, section 3.0).
444
+ // The boundary itself is unchanged and is now enforced negatively: no message
445
+ // here may assert who or what wrote the text. See the claim-boundary tests in
446
+ // tests/core/unit/patterns-v2.test.mjs and patterns-v3.test.mjs.
447
+ export const CATEGORY_META = {
448
+ "tier1": { severity: "high", message: "A word that turns up a lot in generic machine-written copy, like \"delve\", \"robust\" or \"seamless\".", suggestion: "Swap it for a plainer word." },
449
+ "tier1-clarity": { severity: "medium", message: "This is a long way of saying something short, like \"in order to\" for \"to\", or \"due to the fact that\" for \"because\".", suggestion: "Use the shorter form." },
450
+ "tier2": { severity: "medium", message: "Several buzzwords sit in one paragraph, words like \"foster\", \"facilitate\" and \"ecosystem\". One is fine. A pile of them reads as filler.", suggestion: "Keep one at most, and say the plain thing instead." },
451
+ "tier3": { severity: "low", message: "A vague filler word such as \"significant\", \"effective\" or \"dynamic\" is used a lot for a piece this long.", suggestion: "Cut some of them, or say what you actually mean." },
452
+ "transition": { severity: "medium", message: "A joining phrase that could link any two sentences, like \"Moreover\", \"Furthermore\" or \"In conclusion\".", suggestion: "Cut it, or say how the two sentences really connect." },
453
+ "chatbot": { severity: "high", message: "A line from a chat window is still in the text, like \"I hope this helps\" or \"Certainly!\".", suggestion: "Delete it." },
454
+ "sycophantic": { severity: "high", message: "A compliment aimed at the reader's question, the way a chatbot opens a reply: \"Great question!\".", suggestion: "Delete it and go straight to the point." },
455
+ "filler": { severity: "medium", message: "A phrase that adds words but no meaning.", suggestion: "Cut it. The sentence almost always still works." },
456
+ "generic-conclusion": { severity: "medium", message: "A closing line that could end any article, like \"only time will tell\" or \"the future looks bright\".", suggestion: "End on something specific instead." },
457
+ "lets-construction": { severity: "medium", message: "A \"let's take a look at…\" line, the way a tutorial talks to you.", suggestion: "Just say the thing. Do not announce it first." },
458
+ "reasoning-artifact": { severity: "high", message: "Step-by-step working is still showing, like \"First, let me…\" or \"Step 1:\".", suggestion: "Delete the working and keep the answer." },
459
+ "acknowledgment-loop": { severity: "medium", message: "The text repeats the question back before answering it: \"You asked about X. X is…\".", suggestion: "Answer straight away." },
460
+ "significance-inflation": { severity: "high", message: "A stock line telling the reader how big something was, instead of saying what happened: \"marking a pivotal moment\".", suggestion: "Say what happened and let the reader judge." },
461
+ "vague-attribution": { severity: "high", message: "A claim is credited to nobody in particular: \"experts say\", \"studies show\".", suggestion: "Name the study or the person, or drop the claim." },
462
+ "hollow-intensifier": { severity: "medium", message: "An emphasis word that adds no information, like \"truly\", \"incredibly\" or \"absolutely\".", suggestion: "Cut it." },
463
+ "emotional-flatline": { severity: "low", message: "A ready-made feeling word stands in for a real reaction, like \"fascinating\" or \"surprising\".", suggestion: "Say what was actually surprising, or cut it." },
464
+ "lingering-attention": { severity: "medium", message: "A share-post line about how long an idea stayed with you, which tells the reader nothing about the idea: \"this stuck with me for days\".", suggestion: "Say why it matters instead." },
465
+ "novelty-inflation": { severity: "medium", message: "The text announces that it is about to say something rare: \"few people realise\".", suggestion: "Show the point. Do not advertise it." },
466
+ "cutoff-disclaimer": { severity: "high", message: "A line where a chatbot describes itself or its training cut-off, like \"as of my last update\" or \"as an AI language model\".", suggestion: "Delete it, and check the facts around it yourself." },
467
+ "template-phrase": { severity: "high", message: "A ready-made phrase that turns up in a lot of generated copy.", suggestion: "Replace it with something specific to your subject." },
468
+ "false-concession": { severity: "medium", message: "A formula that pretends to give ground before making the point: \"while it's true that…\".", suggestion: "Name the real trade-off, or drop it." },
469
+ "rhetorical-question": { severity: "medium", message: "A question the writer asks and then answers: \"So what does this mean?\".", suggestion: "Give the answer without asking first." },
470
+ "confidence-calibration": { severity: "low", message: "Words like \"clearly\", \"certainly\" and \"undoubtedly\" pile up across the text.", suggestion: "Keep the one or two you have earned and cut the rest." },
471
+ "em-dash-density": { severity: "medium", message: "Em dashes (—), or spaced hyphens used as dashes, turn up a lot for a piece this long. Worth knowing: since OpenAI cut back on em dashes in late 2025, heavy dash use is more common in Claude's writing than in machine writing generally, and plenty of professional human writers use more dashes than this.", suggestion: "Swap some for commas, colons or full stops." },
472
+ "not-just-contrast": { severity: "high", message: "The \"it isn't just X, it's Y\" shape.", suggestion: "Say what it is, without the set-up." },
473
+ "uniform-sections": { severity: "medium", message: "Every section is close to the same length. Real writing runs long in places and short in others.", suggestion: "Let each section be as long as its content needs." },
474
+ "uniform-list-items": { severity: "medium", message: "The list items are all close to the same length. Real lists are lumpy.", suggestion: "Let some items run shorter or longer, or merge ones that repeat." },
475
+ "sentence-flatline": { severity: "medium", message: "Sentence lengths barely change across the whole piece. Real writing swings between short and long.", suggestion: "Mix a few short, punchy sentences in with the longer ones." },
476
+ "uniformity": { severity: "medium", message: "The paragraphs are all close to the same size.", suggestion: "Make some paragraphs shorter or longer on purpose." },
477
+ "formatting": { severity: "medium", message: "A lot of bold is used.", suggestion: "Take the bold off most phrases, and put the important thing first." },
478
+ "tier3-phrase": { severity: "medium", message: "The same stock phrase keeps coming back through the text.", suggestion: "Replace at least one of them with something concrete." },
479
+ "tier3-phrase-cluster": { severity: "high", message: "Several different stock phrases are stacked up in one piece.", suggestion: "Rewrite it around one real point." },
480
+ "hashtag-stuff": { severity: "medium", message: "A long block of hashtags.", suggestion: "Cut to two or three that fit, or none." },
481
+ "bullet-np-list": { severity: "high", message: "A long bullet list where every item is a bare noun phrase with no verb.", suggestion: "Turn it into a paragraph, or join the items up." },
482
+ "hedge-stack": { severity: "high", message: "Two hedges on the same claim, like \"may potentially\" or \"could possibly\".", suggestion: "Pick one hedge, or make a claim someone could check." },
483
+ "future-narrative": { severity: "high", message: "A vague line about the future that nobody could ever check: \"is set to reshape the industry\".", suggestion: "Say something concrete a reader could test." },
484
+ "real-actual-inflation": { severity: "medium", message: "\"Real\" or \"actual\" is doing no work here, as in \"real value\" or \"actual impact\".", suggestion: "Cut the word, or say what makes it real." },
485
+ "social-cta-closer": { severity: "high", message: "A closing line begging for engagement: \"What do you think? Let me know below.\"", suggestion: "Give the reader a reason to care instead of an instruction." },
486
+ "formulaic-opener": { severity: "high", message: "A stock opening line that would fit any article: \"In today's fast-paced world…\".", suggestion: "Open on a fact or a specific detail." },
487
+ "speculative-opener": { severity: "high", message: "The piece opens by asking you to picture a scene: \"Imagine a world where…\".", suggestion: "Make the point directly." },
488
+ "title-case-header": { severity: "medium", message: "The heading puts a capital on nearly every word, the way a lot of marketing copy does.", suggestion: "Capitalise the first word only, as you would in a sentence." },
489
+ "parenthetical-hedge": { severity: "medium", message: "An aside in brackets that sounds thoughtful and adds nothing: \"(at least in most cases)\".", suggestion: "Cut it, or make it a proper sentence." },
490
+ "smart-punct-signature": { severity: "high", message: "Curly quotes, an em dash, an Oxford comma and no typos, all in one piece. That combination is rare in text somebody typed straight out by hand.", suggestion: "Nothing to fix. This is just something we noticed." },
491
+ "punct-distribution": { severity: "medium", message: "Every paragraph uses about the same amount of punctuation.", suggestion: "Let some paragraphs run busier than others." },
492
+ "fnword-trigram-entropy": { severity: "medium", message: "Sentences are built to the same grammar pattern again and again.", suggestion: "Change the way you build some of the sentences." },
493
+ "cross-para-burstiness": { severity: "medium", message: "Every paragraph has the same inner rhythm of long and short sentences.", suggestion: "Let some paragraphs be clipped and others rambling." },
494
+ "normalization-flag": { severity: "high", message: "The text held invisible characters or lookalike letters, the kind used to hide copied text from checkers. We swapped them back before reading it.", suggestion: "Take the hidden characters out and check the text again." },
495
+ "low-ttr": { severity: "low", message: "The same words come round a lot for a piece this long.", suggestion: "Vary the nouns and verbs, unless the subject really does need the repeating." },
496
+ "ai-placeholder": { severity: "high", message: "A blank somebody forgot to fill in is still here, like \"[INSERT NAME]\".", suggestion: "Fill it in or delete it before this goes out." },
497
+ "ai-citation-markup": { severity: "high", message: "Chatbot citation code was pasted in along with the text.", suggestion: "Delete it and put a real reference in its place." },
498
+ "ai-utm-source": { severity: "high", message: "A link carries a tracking tag that AI tools add to the links they hand out, like \"utm_source=chatgpt.com\".", suggestion: "Strip the tracking part off the link." },
499
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * en-signals v2 — editorial writing-signal engine.
3
+ *
4
+ * TypeScript adaptation of the MIT-licensed `avoid-ai-writing` detection
5
+ * engine (`detector/patterns.js`, Conor Bronsdon and contributors), snapshotted
6
+ * in `source-snapshots/avoid-ai-writing/`. Attribution: THIRD_PARTY_NOTICES.md.
7
+ *
8
+ * Scope and claim boundary (BRIEF.md §5, §21): everything this module reports
9
+ * is Tier B evidence — documented writing-pattern rules and stylometric
10
+ * measurements. It describes style. It never proves, and must never be
11
+ * presented as proving, who or what wrote a text.
12
+ *
13
+ * Adaptation notes (deliberate deviations from upstream):
14
+ * - Findings carry exact spans into the ORIGINAL input text via an offset map
15
+ * maintained through the normalisation pre-pass, so `matched_text_hash`
16
+ * always hashes the literal input slice.
17
+ * - The Markdown-blockquote strip pre-pass is omitted: this engine runs on
18
+ * projected visible text, and dropping lines would break span fidelity.
19
+ * - The zero-weight `unnecessary-hyphenation` copyedit family (and its quote/
20
+ * path masking helpers) is not ported; it never affects the score and is a
21
+ * grammar copyedit rather than a writing signal.
22
+ * - Sentence-region smoothing (UI highlighting) is not ported; spans on each
23
+ * finding replace it.
24
+ * - `certainly!`/`absolutely!`/`great question!`/`excellent point!` use a
25
+ * trailing `\B` where upstream used `\b`, which could never assert after a
26
+ * closing `!` followed by a space — the canonical shape of the artefact.
27
+ */
28
+ import type { PatternFinding } from "@opacedev/ai-content-checker-contracts";
29
+ export declare const EN_SIGNALS_PATTERN_VERSION = "en-signals:2026.08.6";
30
+ export type SignalsClassification = "human_like" | "mixed_signals" | "ai_like";
31
+ export interface EditorialSignalsResult {
32
+ /** 0–100 log-normalised editorial-signals score. Higher = more documented style signals. */
33
+ score: number;
34
+ /** Trinary, false-negative-biased style classification. Stylistic evidence only, never authorship proof. */
35
+ classification: SignalsClassification;
36
+ /** Soft class probabilities summing to 1. Not calibrated against a labelled corpus. */
37
+ probabilities: {
38
+ human_like: number;
39
+ mixed_signals: number;
40
+ ai_like: number;
41
+ };
42
+ /** low / medium / high confidence band for the classification. */
43
+ confidence: "low" | "medium" | "high";
44
+ /** Distinct signal categories that fired, sorted alphabetically. */
45
+ categoriesHit: string[];
46
+ /** Count of distinct deduplicated signal hits. */
47
+ findingCount: number;
48
+ wordCount: number;
49
+ version: string;
50
+ /** "scored" or the reason the text was not meaningfully assessed. */
51
+ status: "scored" | "empty" | "too_short" | "too_long";
52
+ /**
53
+ * 2026.08.4 escalation policy (additive). `applied` names the documented
54
+ * escalation that raised the classification above the argmax base, or null
55
+ * when the classification IS the argmax. `reason` is a plain-English
56
+ * explanation suitable for a UI. Escalations only ever raise, never lower.
57
+ */
58
+ escalation: {
59
+ applied: string | null;
60
+ reason: string;
61
+ };
62
+ /** Plain-English claim boundary. Always states this is stylistic evidence, not authorship proof. */
63
+ description: string;
64
+ }
65
+ /**
66
+ * Run the full en-signals v2 rule set and return per-signal findings.
67
+ * Findings are editorial hints (Tier B evidence): stylistic, never authorship proof.
68
+ */
69
+ export declare function inspectSignalsV2(text: string): PatternFinding[];
70
+ /**
71
+ * Document-level editorial-signals score, ported from the upstream weighting
72
+ * and log-normalisation model. The score, classification and probabilities are
73
+ * stylistic evidence about how the text reads; they are never proof of
74
+ * authorship (BRIEF.md §5, §21 Tier B).
75
+ */
76
+ export declare function computeEditorialSignals(text: string): EditorialSignalsResult;