@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,614 @@
1
+ /**
2
+ * Rule data for the en-signals 2026.08.3 harvest merge.
3
+ *
4
+ * Sources merged here (see research/AI-TELLS-MEGA-PACK.md, research/
5
+ * ai-tells-pack-seed.json `tells-seed:2026.08.1`, and research/
6
+ * OWNER-DOCS-TELLS.md):
7
+ * - Tier A tells ship at their stated severity.
8
+ * - Tier B tells ship at low severity with `corroboration: true` metadata —
9
+ * they are weak alone and only meaningful alongside other findings.
10
+ * - Tier C tells are NOT implemented; they are recorded in EXCLUDED_TELLS so
11
+ * coverage is auditable.
12
+ *
13
+ * Binding corrections from the research (do not "fix" these without reading
14
+ * the mega-pack):
15
+ * - No naive hedging rule: epistemic hedges DECREASED in post-LLM text
16
+ * (arXiv:2603.16131, 2505.09662); a hedge-word counter fires backwards.
17
+ * - Question-heading / uniform-FAQ shapes stay corroboration-weight only:
18
+ * SEO/AEO guides teach humans the identical template.
19
+ * - Stylometric measures never dominate the document score (cap applied in
20
+ * en-signals-v2.ts) — the Stanford TOEFL study found >50% of genuine
21
+ * non-native essays falsely flagged by stylometric detectors.
22
+ *
23
+ * Licensing: regex/list data here is either (a) adapted from Apache-2.0/MIT
24
+ * sources (sam-paech/antislop-sampler, slop-forensics, SLOP_Detector,
25
+ * hwajongpark/slop-gate, avectats7/anti-ai-writing, kjmagnan1s/anti-slop,
26
+ * aplaceforallmystuff/claude-slop-detector), (b) uncopyrightable facts from
27
+ * academic papers (Liang et al. 2024, Kobak et al. 2025, Juzek & Ward 2025,
28
+ * Reinhart et al. PNAS 2025, Pew 2026), (c) independently re-expressed from
29
+ * CC BY-SA Wikipedia guidance, or (d) the owner's own documents. AGPL
30
+ * (AlpinDale/gptslop) and unlicensed (jalaalrd) lists were NOT copied; where
31
+ * the same observations appear they were reimplemented from the underlying
32
+ * facts. See THIRD_PARTY_NOTICES.md.
33
+ *
34
+ * Everything remains Tier B evidence in BRIEF.md §21 terms: editorial hints
35
+ * about style — never proof of authorship. The artefact-forensics category is
36
+ * the strongest evidence class (near-zero false positives, model-attributing)
37
+ * but is still reported as evidence, not verdict.
38
+ */
39
+ export const RULE_ERA = {
40
+ // ── existing v2 categories ──
41
+ "tier1": { era: "2023" },
42
+ "tier1-clarity": { era: "evergreen" },
43
+ "tier2": { era: "2023" },
44
+ "tier3": { era: "2023" },
45
+ "transition": { era: "2023" },
46
+ "chatbot": { era: "evergreen", attribution: "multi" },
47
+ "sycophantic": { era: "evergreen", attribution: "multi" },
48
+ "filler": { era: "2023" },
49
+ "generic-conclusion": { era: "2023" },
50
+ "lets-construction": { era: "evergreen" },
51
+ "reasoning-artifact": { era: "2025-26" },
52
+ "acknowledgment-loop": { era: "evergreen" },
53
+ "significance-inflation": { era: "2024-25" },
54
+ "vague-attribution": { era: "evergreen" },
55
+ "hollow-intensifier": { era: "evergreen" },
56
+ "emotional-flatline": { era: "2024-25" },
57
+ "lingering-attention": { era: "2025-26" },
58
+ "novelty-inflation": { era: "2025-26" },
59
+ "cutoff-disclaimer": { era: "2023", attribution: "multi" },
60
+ "template-phrase": { era: "2023" },
61
+ "false-concession": { era: "2024-25" },
62
+ "rhetorical-question": { era: "2024-25" },
63
+ "confidence-calibration": { era: "evergreen" },
64
+ // Post-2025 nuance: OpenAI suppressed em dashes in Nov 2025; by mid-2026
65
+ // only Claude-family output exceeds professional writers, making density a
66
+ // partial model-attribution hint rather than a generic AI tell.
67
+ "em-dash-density": { era: "2025-26", attribution: "claude" },
68
+ "not-just-contrast": { era: "2024-25" },
69
+ "uniform-sections": { era: "evergreen" },
70
+ "uniform-list-items": { era: "evergreen" },
71
+ "sentence-flatline": { era: "2023" },
72
+ "uniformity": { era: "evergreen" },
73
+ "formatting": { era: "evergreen", attribution: "multi" },
74
+ "tier3-phrase": { era: "2023" },
75
+ "tier3-phrase-cluster": { era: "2023" },
76
+ "hashtag-stuff": { era: "evergreen" },
77
+ "bullet-np-list": { era: "evergreen" },
78
+ "hedge-stack": { era: "evergreen" },
79
+ "future-narrative": { era: "2024-25" },
80
+ "real-actual-inflation": { era: "2025-26" },
81
+ "social-cta-closer": { era: "2025-26" },
82
+ "formulaic-opener": { era: "2023" },
83
+ "speculative-opener": { era: "2023" },
84
+ "title-case-header": { era: "evergreen" },
85
+ "parenthetical-hedge": { era: "2025-26" },
86
+ "smart-punct-signature": { era: "evergreen" },
87
+ "punct-distribution": { era: "evergreen" },
88
+ "fnword-trigram-entropy": { era: "evergreen" },
89
+ "cross-para-burstiness": { era: "evergreen" },
90
+ "normalization-flag": { era: "evergreen" },
91
+ "low-ttr": { era: "2023" },
92
+ "ai-placeholder": { era: "evergreen", attribution: "multi" },
93
+ "ai-citation-markup": { era: "2025-26", attribution: "multi" },
94
+ "ai-utm-source": { era: "2024-25", attribution: "multi" },
95
+ // ── v3 artefact forensics ──
96
+ "ai-citation-token": { era: "2025-26", attribution: "multi" },
97
+ "reasoning-leak": { era: "2025-26" },
98
+ "placeholder-token": { era: "2024-25" },
99
+ "pua-character": { era: "2025-26", attribution: "chatgpt" },
100
+ "math-alphanumeric": { era: "2025-26", attribution: "chatgpt" },
101
+ "arrow-decoration": { era: "2025-26", attribution: "chatgpt" },
102
+ "escaped-markup-literal": { era: "evergreen", attribution: "multi" },
103
+ // ── v3 tier A phrase/structural ──
104
+ "neg-parallelism": { era: "2024-25" },
105
+ "tripled-negation": { era: "2025-26", attribution: "chatgpt" },
106
+ "despite-challenges-arc": { era: "2023" },
107
+ "metaphor-cluster": { era: "2023" },
108
+ "participial-tail": { era: "evergreen" },
109
+ "focal-density": { era: "2023" },
110
+ "owner-phrase": { era: "2023" },
111
+ "power-verb-compound": { era: "2023" },
112
+ "outcome-tail": { era: "2023" },
113
+ "conclusion-cta": { era: "2023" },
114
+ // ── v3 tier B (corroboration-weight) ──
115
+ "liang-cluster": { era: "2024-25" },
116
+ "kobak-density": { era: "2023" },
117
+ "promo-travel": { era: "2023" },
118
+ "pivotal-role": { era: "2024-25" },
119
+ "legacy-framing": { era: "2024-25", attribution: "multi" },
120
+ "notability-canned": { era: "2025-26" },
121
+ "buzzword-phrase": { era: "2023" },
122
+ "faux-insight": { era: "2025-26" },
123
+ "rhetorical-qa": { era: "2024-25", attribution: "chatgpt" },
124
+ "didactic-note": { era: "2023" },
125
+ "narrative-cliche": { era: "2024-25" },
126
+ "valuable-insights": { era: "2024-25" },
127
+ "copula-avoidance": { era: "evergreen" },
128
+ "bold-label-bullets": { era: "evergreen", attribution: "multi" },
129
+ "emoji-decoration": { era: "2024-25", attribution: "chatgpt" },
130
+ "heading-inflation": { era: "2024-25", attribution: "gemini" },
131
+ "staccato-fragments": { era: "2025-26" },
132
+ "tricolon-density": { era: "evergreen" },
133
+ "transition-stacking": { era: "2023" },
134
+ "quote-inconsistency": { era: "2025-26", attribution: "chatgpt" },
135
+ "token-cutoff": { era: "2023", attribution: "chatgpt" },
136
+ "setup-expansion-cadence": { era: "2025-26" },
137
+ "passive-ratio": { era: "evergreen" },
138
+ "low-specificity": { era: "evergreen" },
139
+ "adjacent-lemma-repeat": { era: "evergreen" },
140
+ "fiction-claudeism": { era: "2024-25", attribution: "claude" },
141
+ "fiction-promptonym": { era: "2024-25", attribution: "multi" },
142
+ "fiction-slop-phrase": { era: "2024-25", attribution: "multi" },
143
+ "owner-phrase-b": { era: "2023" },
144
+ "owner-vocab-b": { era: "2023" },
145
+ "directive-colon-bullets": { era: "2024-25" },
146
+ "teach-preach-headings": { era: "2023" },
147
+ "by-ving-template": { era: "2023" },
148
+ "invalid-isbn": { era: "evergreen" },
149
+ "proximity-cluster": { era: "evergreen" },
150
+ // ── 2026.08.6 provider-eval furniture rules ──
151
+ // deepseek 99.3% / google-25 95.3% / mistral 94.7% carry bold; heaviest in
152
+ // the 2024+ chat register across vendors → multi attribution.
153
+ "markdown-bold": { era: "2024-25", attribution: "multi" },
154
+ "markdown-heading": { era: "2025-26", attribution: "multi" },
155
+ "markdown-furniture": { era: "2024-25", attribution: "multi" },
156
+ };
157
+ /** Categories whose findings are corroboration-weight only (research Tier B). */
158
+ export const CORROBORATION_CATEGORIES = new Set([
159
+ "liang-cluster", "kobak-density", "promo-travel", "pivotal-role",
160
+ "legacy-framing", "notability-canned", "buzzword-phrase", "faux-insight",
161
+ "rhetorical-qa", "didactic-note", "narrative-cliche", "valuable-insights",
162
+ "copula-avoidance", "bold-label-bullets", "emoji-decoration",
163
+ "heading-inflation", "staccato-fragments", "tricolon-density",
164
+ "transition-stacking", "quote-inconsistency", "token-cutoff",
165
+ "setup-expansion-cadence", "passive-ratio", "low-specificity",
166
+ "adjacent-lemma-repeat", "fiction-claudeism", "fiction-promptonym",
167
+ "fiction-slop-phrase", "owner-phrase-b", "owner-vocab-b",
168
+ "directive-colon-bullets", "teach-preach-headings", "by-ving-template",
169
+ "invalid-isbn", "proximity-cluster", "escaped-markup-literal",
170
+ // 2026.08.6: markdown-furniture rules are corroboration-weight by design —
171
+ // their absence must never count in favour of a human verdict, because an
172
+ // editor paste that strips formatting removes the signal entirely
173
+ // (PROVIDER-EVAL-2026-08.md §1 honest gaps, §4.1 caveats). Their
174
+ // classification power comes from the zero-FP escalation floors, not from
175
+ // severity or weight.
176
+ "markdown-bold", "markdown-heading", "markdown-furniture",
177
+ ]);
178
+ // ─── 2026.08.4 escalation-policy category sets ───────────────────────
179
+ // Derived from the real-world evaluation (research/REAL-WORLD-EVAL-2026-08.md
180
+ // §4a): the rule tier produced artefact evidence on 7/7 artefact-bearing AI
181
+ // samples yet classified almost all of them human_like. These sets feed the
182
+ // post-scoring escalation policy in en-signals-v2.ts. Human controls fired
183
+ // none of these categories (0/4), so the policy adds no human FP risk.
184
+ /**
185
+ * Tier-A artefact-forensics categories that alone justify flooring the
186
+ * classification at mixed_signals (near-zero FP; none fired on any human
187
+ * control in the evaluation).
188
+ */
189
+ export const ARTEFACT_CORE_CATEGORIES = new Set([
190
+ "ai-citation-markup", "ai-citation-token", "ai-utm-source",
191
+ "reasoning-leak", "placeholder-token", "ai-placeholder",
192
+ "pua-character", "math-alphanumeric",
193
+ ]);
194
+ /**
195
+ * Artefact-adjacent categories that count toward the floor only when
196
+ * co-occurring with other artefact evidence — the evaluation kept
197
+ * arrow-decoration corroboration-only on its own (arrows are common in
198
+ * genuine technical notes; eval §4b).
199
+ */
200
+ export const ARTEFACT_SUPPORT_CATEGORIES = new Set([
201
+ "arrow-decoration", "escaped-markup-literal",
202
+ ]);
203
+ /**
204
+ * Chat-export formatting furniture: three or more of these together form the
205
+ * formatting-cluster compound (eval §4a item 4; evidence opace-openai-006).
206
+ */
207
+ export const FORMATTING_CLUSTER_CATEGORIES = new Set([
208
+ "bold-label-bullets", "heading-inflation", "emoji-decoration", "arrow-decoration",
209
+ ]);
210
+ // ─── 2026.08.6 provider-eval furniture calibration ───────────────────
211
+ // Measured thresholds from services/local-engine/research/provider-eval/
212
+ // PROVIDER-EVAL-2026-08.md §4.1 (1,727 AI + 169 held-out humans; every
213
+ // number below fired on 0/169 humans, 0/10 business-marketing humans).
214
+ // The bullets rate is R5's measured gate; bold/heading fire on ANY
215
+ // occurrence because both occurred in 0/169 human documents.
216
+ export const V6_FURNITURE_THRESHOLDS = {
217
+ /** R5: bullet lines per 1,000 words above which bullets alone open the gate. */
218
+ bulletsPer1000: 10.75,
219
+ };
220
+ /**
221
+ * Stylometric measurement categories. Their combined contribution to the
222
+ * document score is capped in en-signals-v2.ts so stylometrics can never
223
+ * dominate a verdict (research correction: non-native-writer false-positive
224
+ * landmine; Stanford TOEFL study).
225
+ */
226
+ export const STYLOMETRIC_CATEGORIES = new Set([
227
+ "punct-distribution", "cross-para-burstiness", "fnword-trigram-entropy",
228
+ "sentence-flatline", "uniformity", "uniform-sections", "uniform-list-items",
229
+ "low-ttr", "smart-punct-signature", "em-dash-density",
230
+ "setup-expansion-cadence", "passive-ratio", "low-specificity",
231
+ "adjacent-lemma-repeat", "copula-avoidance", "tricolon-density",
232
+ "staccato-fragments", "transition-stacking", "heading-inflation",
233
+ ]);
234
+ export const AI_CITATION_TOKENS = [
235
+ { pattern: /【\d+†L\d+(?:-L?\d+)?】/g, attribution: "deepseek" },
236
+ { pattern: /\bgrok_render_citation_card_json\b/gi, attribution: "grok" },
237
+ { pattern: /\bgrok-card\s+data-id\b/gi, attribution: "grok" },
238
+ { pattern: /ppl-ai-file-upload/gi, attribution: "perplexity" },
239
+ { pattern: /\[attached_file:\d+\]/gi, attribution: "perplexity" },
240
+ { pattern: /\[web:\d+\]/g, attribution: "perplexity" },
241
+ { pattern: /\[cite:\s*\d+\]/g, attribution: "gemini" },
242
+ { pattern: /\[span_\d+\]\(start_span\)/g, attribution: "gemini" },
243
+ { pattern: /\battributableIndex\b/g, attribution: "chatgpt" },
244
+ { pattern: /:::writing\{/g, attribution: "chatgpt" },
245
+ { pattern: /\bciteturn\d+(?:search|image|news|navigation)\d+/gi, attribution: "chatgpt" },
246
+ ];
247
+ export const REASONING_LEAKS = [
248
+ /\bthe\s+user\s+(?:wants|is\s+asking|requested|has\s+asked)\b/gi,
249
+ /\breviewer\s+note\s*[:—-]/gi,
250
+ /\bas\s+per\s+(?:the\s+)?(?:system\s+)?prompt\b/gi,
251
+ /\bso\s+i\s+should\s+(?:structure|frame|word)\s+(?:the|this|my)\b/gi,
252
+ ];
253
+ export const PLACEHOLDER_TOKENS = [
254
+ /\bINSERT_[A-Z][A-Z_]{2,40}\b/g,
255
+ /\bPASTE_[A-Z][A-Z_]{2,40}\b/g,
256
+ /\baccess-date\s*=\s*\d{4}-XX-XX\b/gi,
257
+ ];
258
+ // Pure codepoint checks implemented HERE as pattern rules because
259
+ // packages/core/src/unicode/ is owned by another workstream — the unicode
260
+ // module reports carriers/confusables; these rules report the pattern-layer
261
+ // editorial reading of the same characters. Overlap is deliberate and noted
262
+ // in the workstream report.
263
+ export const PUA_RANGE_RE = /[\uE000-\uF8FF]/g;
264
+ export const MATH_ALPHANUMERIC_RE = /[\u{1D400}-\u{1D7FF}]/gu;
265
+ /** Spaced arrow used as a prose connector ("input → output"). */
266
+ export const ARROW_CONNECTOR_RE = /(?<=\S)\s[\u2192\u2794\u27A1]\s(?=\S)/g;
267
+ export const ESCAPED_MARKUP_LITERALS = [
268
+ /&nbsp;/g,
269
+ /(?<!\\)\\n\\n/g,
270
+ ];
271
+ // ─── Tier A phrase / structural rules ────────────────────────────────
272
+ // Negative parallelism, non-dash form ("not only X but also Y"). Flagged only
273
+ // at 2+ occurrences per document (Pew 2026: ~3x human rate; JFK used it too).
274
+ export const NEG_PARALLELISM_RE = /\bnot\s+(?:just|only|merely)\s+[^.!?\n]{2,60}?,?\s+but\s+(?:also\s+|rather\s+)?\w+/gi;
275
+ /** "Not X. Not Y. Just Z." tripled negation (ChatGPT-family favourite). */
276
+ export const TRIPLED_NEGATION_RE = /\b(?:Not|No)\s+[^.!?,\n]{2,30}[.,]\s*(?:Not|no)\s+[^.!?,\n]{2,30}[.,]\s*(?:Just|just)\s+\w+/g;
277
+ /** The rigid "Despite challenges … continues to thrive" essay arc. */
278
+ export const DESPITE_CHALLENGES_RE = /\bDespite\s+(?:these|its|numerous|various|several)\s+(?:challenges|setbacks|obstacles)\b[^.!?\n]{0,120}?(?:continues?\s+to\s+(?:thrive|grow|flourish)|faces?\s+(?:several\s+|numerous\s+)?challenges)/gi;
279
+ /** Figurative metaphor cluster — flag 2+ DISTINCT items per document. */
280
+ export const METAPHOR_CLUSTER_RES = [
281
+ /\b(?:rich\s+tapestry|tapestry\s+of)\b/gi,
282
+ /\b(?:complex|intricate)\s+interplay\b/gi,
283
+ /\b(?:ever-)?evolving\s+landscape\b/gi,
284
+ /\ba\s+testament\s+to\b/gi,
285
+ /\bbeacon\s+of\b/gi,
286
+ ];
287
+ /**
288
+ * Sentence-final present-participle significance tail (Reinhart et al. PNAS
289
+ * 2025; WP:SUPERFICIAL). Flagged only at 3+ per document — one -ing tail is
290
+ * ordinary English.
291
+ */
292
+ export const PARTICIPIAL_TAIL_RE = /,\s+(?:highlighting|underscoring|reflecting|symboli[sz]ing|showcasing|emphasi[sz]ing|demonstrating|ensuring|fostering|solidifying|cementing|signaling|signalling|contributing\s+to)\b[^.!?\n]{5,80}[.!?]/g;
293
+ /**
294
+ * Focal-word density (Juzek & Ward COLING 2025; Kobak Science Advances 2025;
295
+ * Liang ICML 2024; Pew 2026 — the triple-validated core lexicon). A DENSITY
296
+ * rule: fires once per document when 3+ hits land at 3+ per 1,000 words.
297
+ */
298
+ export const FOCAL_WORD_RE = /\b(?:delv(?:e|es|ed|ing)|showcas(?:e|es|ing)|boasts?|underscor(?:e|es|ing)|intricac(?:y|ies)|intricate(?:ly)?|surpass(?:es|ing)|garner(?:ed|s)?|emphasi[sz]ing|groundbreaking|meticulous(?:ly)?|commendable|pivotal|elucidat(?:e|es|ing)|advancements)\b/gi;
299
+ // Owner Tier A phrases (OWNER-DOCS-TELLS.md §8 merge plan; provenance kept in
300
+ // one category so the owner pack stays traceable against the web pack).
301
+ export const OWNER_PHRASES = [
302
+ /\blook\s+no\s+further\b/gi,
303
+ /\bcomprehensive\s+suite\s+of\b/gi,
304
+ /\btailored\s+solutions?\b/gi,
305
+ /\bunlock(?:ing)?\s+(?:its|your|their|the)\s+full\s+potential\b/gi,
306
+ /\bbefore\s+diving\s+in(?:to)?\b/gi,
307
+ /\bcommon\s+pitfalls\s+and\s+how\s+to\s+avoid\s+them\b/gi,
308
+ /\b(?:break(?:ing)?\s+(?:down\s+)?the\s+process\s+(?:down\s+)?into|process\s+can\s+be\s+broken\s+down\s+into)\b/gi,
309
+ /\bunderstanding\s+your\s+starting\s+point\b/gi,
310
+ /\bwhile\s+it\s+might\s+seem\s+counterintuitive\b/gi,
311
+ /\bcommon\s+choices\s+include\b/gi,
312
+ /\bwhere\s+things\s+get\s+interesting\b/gi,
313
+ /\bright\?\s*Well,\s+not\s+any\s*more\b/gi,
314
+ /\bthe\s+future\s+of\s+[\w\s]{1,30}\s+is\s+increasingly\s+(?:shaped|driven|defined)\s+by\b/gi,
315
+ /\btake\s+action\s+now\b/gi,
316
+ /\banother\s+(?:important|key|crucial)\s+(?:aspect|factor|thing|element|point)\s+to\s+consider\b/gi,
317
+ /\bby\s+doing\s+so,?\s+you(?:['’]ll|\s+can|\s+will)\b/gi,
318
+ /\bit['’]?s\s+(?:crucial|important|essential)\s+to\s+remember\b/gi,
319
+ /\bhere\s+are\s+(?:\d+|five|six|seven|eight|nine|ten)\s+(?:simple\s+|key\s+|essential\s+)?(?:steps|things|ways|tips|strategies|best\s+practices|key\s+elements)\b/gi,
320
+ /\bkey\s+(?:elements|aspects|factors|components|considerations)\s+include\b/gi,
321
+ /\b(?:to\s+)?put\s+it\s+simply\b/gi,
322
+ /\bescape\s+the\s+stresses\s+of\s+daily\s+life\b/gi,
323
+ /\bworks?\s+wonders\b/gi,
324
+ ];
325
+ /** Power verb within a few tokens of an intangible buzz-adjective (owner §3e). */
326
+ export const POWER_VERB_COMPOUND_RE = /\b(?:ensur(?:e|es|ing)|leverag(?:e|es|ing)|prioriti[sz](?:e|es|ing)|harness(?:es|ing)?|enhanc(?:e|es|ing)|capitali[sz](?:e|es|ing)\s+on|deliver(?:s|ing)?)\s+(?:\w+\s+){0,3}(?:holistic|seamless|robust|sustainable|scalable|exceptional|strategic|transformative)\b/gi;
327
+ /** ", leading to increased/improved X" outcome tail (owner §3f). */
328
+ export const OUTCOME_TAIL_RE = /,\s*leading\s+to\s+(?:increased|improved|enhanced|greater|better)\s+\w+/gi;
329
+ /** Compound marketing conclusion: "by following these steps … boost/improve". */
330
+ export const CONCLUSION_CTA_RE = /\bby\s+following\s+these\s+(?:steps|tips|strategies|best\s+practices)\b[^.!?\n]{0,80}?\b(?:boost|improve|enhance|transform|elevate|significantly)\w*/gi;
331
+ // ─── Tier B rules (low severity, corroboration) ──────────────────────
332
+ // Liang et al. 2024 overused evaluative adjectives/adverbs (facts from the
333
+ // paper; words already in TIER1/TIER2 omitted to avoid double counting).
334
+ // Cluster rule: 3+ DISTINCT items per document.
335
+ export const LIANG_CLUSTER_RE = /\b(?:versatile|ingenious|methodical(?:ly)?|insightful|laudable|admirable|profound|intriguing(?:ly)?|cogent|lucid|noteworthy|thoughtfully|judiciously|elegantly|compellingly|synergistically|succinctly|comprehensively|strategically|aptly|hitherto|herein)\b/gi;
336
+ // Kobak et al. style-lexicon subset (excess_words.csv derivation; the full
337
+ // 407-word CSV is not bundled — see workstream report). Cluster: 4+ distinct.
338
+ export const KOBAK_CLUSTER_RE = /\b(?:notable|noteworthy|unparalleled|invaluable|culminating|thereby|garnered|surpassing|groundbreaking|commendable|advancements|encompass(?:es|ing)?)\b/gi;
339
+ // Promotional/travel register cluster (seed `lex-promo-travel`): 2+ distinct.
340
+ export const PROMO_TRAVEL_RE = /\b(?:in\s+the\s+heart\s+of|breathtaking|rich\s+cultural\s+heritage|treasure\s+trove|diverse\s+array|must-visit|hidden\s+gem)\b/gi;
341
+ export const PIVOTAL_ROLE_RE = /\bplays?\s+a\s+(?:crucial|pivotal|vital|key)\s+role\s+in(?:\s+shaping)?\b/gi;
342
+ // Legacy/significance framing (WP:AILEGACY): flagged only when STACKED (2+
343
+ // distinct phrases) — historians and obituarists use singles legitimately.
344
+ export const LEGACY_FRAMING_RE = /\b(?:pivotal\s+moment|enduring\s+legacy|indelible\s+mark|key\s+turning\s+point|setting\s+the\s+stage\s+for|deeply\s+rooted)\b/gi;
345
+ export const NOTABILITY_CANNED_RE = /\b(?:profiled\s+in\s+multiple\s+outlets|independent\s+coverage|active\s+social\s+media\s+presence|widely-read\s+outlets)\b/gi;
346
+ export const BUZZWORD_PHRASES = [
347
+ /\bunlock(?:ing)?\s+the\s+(?:potential|power)\s+of\b/gi,
348
+ /\bharness(?:ing)?\s+the\s+power\s+of\b/gi,
349
+ /\bembark(?:ing)?\s+on\s+a\s+journey\b/gi,
350
+ /\bat\s+the\s+forefront\s+of\b/gi,
351
+ /\bbridg(?:e|ing)\s+the\s+gap\s+between\b/gi,
352
+ /\bpav(?:e|ing)\s+the\s+way\s+for\b/gi,
353
+ /\bpush(?:ing)?\s+the\s+boundaries\s+of\b/gi,
354
+ /\bnavigat(?:e|ing)\s+the\s+complexit(?:y|ies)\b/gi,
355
+ /\btake\s+(?:it|things)\s+to\s+the\s+next\s+level\b/gi,
356
+ ];
357
+ export const FAUX_INSIGHT_RE = /\b(?:here['’]?s\s+what\s+nobody\s+tells\s+you|what\s+most\s+people\s+get\s+wrong|here['’]?s\s+the\s+kicker|plot\s+twist:|the\s+part\s+everyone\s+misses)\b/gi;
358
+ /** "The result? A platform that scales." — flagged at 2+ per document. */
359
+ export const RHETORICAL_QA_RE = /\b[Tt]he\s+(?:result|goal|answer|solution|problem|catch|best\s+part)\?\s+[A-Z]/g;
360
+ export const DIDACTIC_NOTE_RE = /\b(?:it['’]?s\s+(?:important|crucial|essential)\s+to\s+(?:understand|recognise|recognize)|(?:results|experiences|mileage)\s+may\s+vary|it\s+should\s+be\s+noted)\b/gi;
361
+ export const NARRATIVE_CLICHE_RE = /\b(?:faced\s+numerous\s+challenges|newfound\s+sense\s+of\s+purpose|poignant\s+reminder|serves\s+as\s+a\s+(?:powerful|poignant)\s+reminder)\b/gi;
362
+ export const VALUABLE_INSIGHTS_RE = /\b(?:provid(?:es?|ing)\s+valuable\s+insights?\s+into|key\s+takeaways?\b)/gi;
363
+ /** Copula-avoidance alternatives (Geng & Trotta arXiv:2404.08627). */
364
+ export const COPULA_ALTERNATIVE_RE = /\b(?:serves?|stands?|functions?|operates?)\s+as\s+(?:a|an|the)\b/gi;
365
+ export const BOLD_LABEL_BULLET_RE = /^\s*[-*+•]\s*\*\*[^*\n]{2,40}\*\*[:.]?\s/;
366
+ export const EMOJI_DECOR_RE = /[\u{1F680}\u{2728}\u{1F9E0}\u{2705}\u{1F449}\u{1F4A1}\u{1F3AF}\u{1F525}\u{1F4CC}\u{1F4C8}\u{26A1}\u{1F511}]/u;
367
+ export const RITUAL_HEADING_RE = /^(?:#{1,6}[ \t]+\S.*|<h[1-6][^>]*>.*)$/gim;
368
+ /** ≤4-word staccato fragment run threshold is 3 consecutive. */
369
+ export const STACCATO_MAX_WORDS = 4;
370
+ export const TRANSITION_OPENER_RE = /^\s*(?:Additionally|Moreover|Furthermore|Subsequently|In\s+addition|What['’]s\s+more)\b/i;
371
+ /** Fiction-lane packs (reimplemented from public observations; the AGPL
372
+ * claudeslop.yaml list was NOT copied — see licensing header). */
373
+ export const FICTION_CLAUDEISM_RE = /\b(?:ministrations|audible\s+pop|rivulets\s+of|half-lidded\s+eyes|despite\s+(?:herself|himself)|with\s+reckless\s+abandon|knuckles\s+(?:turning|turned)\s+white|chuckl(?:es|ed)\s+darkly)\b/gi;
374
+ export const FICTION_PROMPTONYM_RE = /\b(?:Elara\s+(?:Voss|Vex)|Aris\s+Thorne|Elias\s+Vance|Whispering\s+(?:Woods|Pines|Hollow)|Eldoria)\b/g;
375
+ // Frequency-ranked fiction phrases (sam-paech/antislop-sampler, Apache-2.0).
376
+ export const FICTION_SLOP_RE = /\b(?:took\s+a\s+deep\s+breath|voice\s+barely\s+above\s+a\s+whisper|couldn['’]?t\s+help\s+but\s+feel|casting\s+long\s+shadows|shivers?\s+(?:ran|run(?:ning)?)\s+down\s+(?:my|her|his|their)\s+spine|heart\s+pounding\s+in\s+(?:her|his|my)\s+chest|the\s+room\s+fell\s+silent|days\s+turned\s+into\s+weeks|maybe,\s+just\s+maybe|little\s+did\s+(?:she|he|they)\s+know|unbeknownst\s+to\s+(?:them|her|him))\b/gi;
377
+ // Owner Tier B phrases (behind corroboration weighting).
378
+ export const OWNER_PHRASES_B = [
379
+ /\bshed(?:s|ding)?\s+light\s+on\b/gi,
380
+ /\ba\s+popular\s+choice\s+for\b/gi,
381
+ /\bconsult(?:ing)?\s+with\s+a\s+(?:professional|specialist|qualified)\b/gi,
382
+ /\bas\s+a\s+professional\s*,/gi,
383
+ /\bstay(?:ing)?\s+ahead\s+of\s+the\s+curve\b/gi,
384
+ /\bI\s+am\s+not\s+a\s+(?:lawyer|doctor|financial\s+advisor|professional)\s*,?\s+but\b/gi,
385
+ /\bsafety\s+should\s+never\s+be\s+(?:overlooked|compromised)\b/gi,
386
+ /\bpresent\s+challenges\s+due\s+to\b/gi,
387
+ /\bextending\s+their\s+lifespan\b/gi,
388
+ /\beven\s+the\s+best\s+plans\s+can\b/gi,
389
+ ];
390
+ /** Owner ban-list vocabulary, tier B (2+ distinct per document). */
391
+ export const OWNER_VOCAB_B_RE = /\b(?:essence|facets?|exhaustive|pesky|folks)\b/gi;
392
+ export const DIRECTIVE_COLON_BULLET_RE = /^\s*(?:[-*+•]|\d+[.)])\s*(?:Plan|Ensure|Optimi[sz]e|Enhance|Leverage|Prioriti[sz]e|Implement|Utili[sz]e|Consider|Embrace)\b[^.:\n]{0,50}:/;
393
+ export const TEACH_PREACH_HEADING_RE = /^#{1,6}\s+(?:why\s+(?:it|this)\s+(?:matters|is\s+important)|how\s+to\s+get\s+started|final\s+thoughts|key\s+takeaways|common\s+pitfalls)\s*\??\s*$/gim;
394
+ export const BY_VING_TEMPLATE_RE = /\bBy\s+\w+ing\s+[^,.\n]{5,60},\s+you\s+can\s+\w+/g;
395
+ /** Passive-voice heuristic: be-form + past participle. */
396
+ export const PASSIVE_RE = /\b(?:is|are|was|were|been|being|be)\s+(?:\w+ly\s+)?\w{3,}(?:ed|en|wn|lt)\b/gi;
397
+ // ─── Weights and metadata ────────────────────────────────────────────
398
+ export const V3_ISSUE_WEIGHTS = {
399
+ // artefact forensics
400
+ "ai-citation-token": 15,
401
+ "reasoning-leak": 12,
402
+ "placeholder-token": 10,
403
+ "pua-character": 14,
404
+ "math-alphanumeric": 12,
405
+ "arrow-decoration": 4,
406
+ "escaped-markup-literal": 3,
407
+ // tier A phrase/structural
408
+ "neg-parallelism": 5,
409
+ "tripled-negation": 5,
410
+ "despite-challenges-arc": 5,
411
+ "metaphor-cluster": 4,
412
+ "participial-tail": 5,
413
+ "focal-density": 5,
414
+ "owner-phrase": 5,
415
+ "power-verb-compound": 6,
416
+ "outcome-tail": 4,
417
+ "conclusion-cta": 6,
418
+ // tier B (low weights; corroboration)
419
+ "liang-cluster": 2,
420
+ "kobak-density": 2,
421
+ "promo-travel": 2,
422
+ "pivotal-role": 2,
423
+ "legacy-framing": 3,
424
+ "notability-canned": 2,
425
+ "buzzword-phrase": 2,
426
+ "faux-insight": 2,
427
+ "rhetorical-qa": 2,
428
+ "didactic-note": 2,
429
+ "narrative-cliche": 3,
430
+ "valuable-insights": 2,
431
+ "copula-avoidance": 3,
432
+ "bold-label-bullets": 3,
433
+ "emoji-decoration": 2,
434
+ "heading-inflation": 3,
435
+ "staccato-fragments": 3,
436
+ "tricolon-density": 2,
437
+ "transition-stacking": 3,
438
+ "quote-inconsistency": 2,
439
+ "token-cutoff": 2,
440
+ "setup-expansion-cadence": 3,
441
+ "passive-ratio": 3,
442
+ "low-specificity": 2,
443
+ "adjacent-lemma-repeat": 3,
444
+ "fiction-claudeism": 3,
445
+ "fiction-promptonym": 3,
446
+ "fiction-slop-phrase": 2,
447
+ "owner-phrase-b": 2,
448
+ "owner-vocab-b": 2,
449
+ "directive-colon-bullets": 3,
450
+ "teach-preach-headings": 2,
451
+ "by-ving-template": 3,
452
+ "invalid-isbn": 3,
453
+ "proximity-cluster": 2,
454
+ // 2026.08.6 furniture rules (low weights; the escalation floors carry the
455
+ // detection, and the weights stay small so furniture cannot fake breadth).
456
+ "markdown-bold": 3,
457
+ "markdown-heading": 3,
458
+ "markdown-furniture": 4,
459
+ };
460
+ /**
461
+ * The paste caveat, shared by the three chat-formatting rules.
462
+ *
463
+ * It is rule-specific honesty, not the generic claim boundary: these three
464
+ * rules can only see chat formatting that survived the paste, so their SILENCE
465
+ * carries no information at all and the message has to say so. Kept as one
466
+ * constant because the three rules make exactly the same point.
467
+ */
468
+ const PASTE = "This only shows up when chat formatting survives the paste. If an editor stripped the formatting, the check finds nothing, which says nothing either way.";
469
+ /** Writing that is formal, or by someone whose first language is not English, reads this way too. */
470
+ const FORMAL = "Formal writing, and writing by people whose first language is not English, can read this way too.";
471
+ export const V3_CATEGORY_META = {
472
+ "ai-citation-token": { severity: "high", message: "A chatbot's own citation code is sitting in the text. The code itself says which chatbot it came from.", suggestion: "Delete the code and put a real reference in its place." },
473
+ "reasoning-leak": { severity: "high", message: "Parts of this text talk about the writing job itself, the kind of notes an AI leaves in its answer, like \"as requested\" or \"let me revise\".", suggestion: "Delete those notes and keep the finished writing." },
474
+ "placeholder-token": { severity: "high", message: "A machine placeholder is still in the text, something like \"{{name}}\" or \"<insert>\".", suggestion: "Fill it in or delete it before this goes out." },
475
+ "pua-character": { severity: "high", message: "The text holds characters from a private corner of Unicode that has no agreed meaning. ChatGPT wraps its citation codes in these. Icon fonts are the only other common reason for them.", suggestion: "Delete them, and look for chatbot citation codes next to them." },
476
+ "math-alphanumeric": { severity: "high", message: "Fake bold or italic letters built from maths symbols (𝗹𝗶𝗸𝗲 𝘁𝗵𝗶𝘀). They come from chatbot copy-paste and social-media text formatters.", suggestion: "Retype them as ordinary letters and use real bold or italic." },
477
+ "arrow-decoration": { severity: "medium", message: "Arrows stand in for words again and again: \"input → output → result\".", suggestion: "Write the connection out in words." },
478
+ "escaped-markup-literal": { severity: "low", message: "A stray scrap of code is showing through the text, like \"&nbsp;\" or \"\\n\". It usually comes from pasting out of a chat window.", suggestion: "Delete it." },
479
+ "neg-parallelism": { severity: "medium", message: "The \"not only X but Y\" shape comes back more than once.", suggestion: "Keep one. Say the rest plainly." },
480
+ "tripled-negation": { severity: "medium", message: "The \"Not X. Not Y. Just Z.\" shape.", suggestion: "Say what it is, without the three-part build-up." },
481
+ "despite-challenges-arc": { severity: "medium", message: "The stock \"despite challenges, it continues to thrive\" story shape.", suggestion: "Name the real problem and what was really done about it." },
482
+ "metaphor-cluster": { severity: "medium", message: "Several worn-out picture words are stacked together: \"tapestry\", \"interplay\", \"evolving landscape\", \"testament to\".", suggestion: "Say the plain facts they are standing in for." },
483
+ "participial-tail": { severity: "medium", message: "Sentences keep ending with a tacked-on \"-ing\" clause that tells you why it mattered: \", highlighting the need for…\", \", underscoring the importance of…\". That ending turns up several times more often in machine writing than in human writing.", suggestion: "Stop the sentence at the fact. Cut the tail, or turn it into a claim you can source." },
484
+ "focal-density": { severity: "medium", message: "A lot of words from the AI-favourite list turn up here: \"delve\", \"showcase\", \"pivotal\", \"meticulous\". Any one of them is normal English. It is how many there are that stands out.", suggestion: "Swap most of them for plainer verbs and adjectives." },
485
+ "owner-phrase": { severity: "medium", message: "A ready-made phrase from the generic-writing phrasebook.", suggestion: "Replace it with something specific to your subject." },
486
+ "power-verb-compound": { severity: "high", message: "A big verb glued to a vague adjective: \"leverage a robust solution\", \"ensure seamless delivery\". It sounds like value and says nothing.", suggestion: "Name the real action and the thing you can measure." },
487
+ "outcome-tail": { severity: "medium", message: "The sentence trails off into a vague result: \", leading to increased engagement\".", suggestion: "Say the exact result, or cut the tail." },
488
+ "conclusion-cta": { severity: "high", message: "The stock marketing sign-off: \"by following these steps you can boost…\".", suggestion: "Close on something specific, not a general promise." },
489
+ "liang-cluster": { severity: "low", message: "Several judgement words from the AI-overuse lists sit close together, like \"crucial\", \"notable\" and \"significant\".", suggestion: "Keep the judgements you can back up with detail." },
490
+ "kobak-density": { severity: "low", message: "Several words sit here that turned up far more often in machine writing when a large body of text was counted.", suggestion: "Vary the words, or back the claims with detail." },
491
+ "promo-travel": { severity: "low", message: "Brochure words are bunched together: \"nestled\", \"breathtaking\", \"hidden gem\". They stand out most when the piece is not a travel brochure.", suggestion: "Describe the place or the product with real detail." },
492
+ "pivotal-role": { severity: "low", message: "The \"plays a crucial role in shaping\" formula.", suggestion: "Say what it actually does." },
493
+ "legacy-framing": { severity: "low", message: "Several grand phrases about legacy and importance are stacked up: \"enduring legacy\", \"pivotal moment\".", suggestion: "Let the events speak for themselves." },
494
+ "notability-canned": { severity: "low", message: "A canned line about how well known something is: \"profiled in multiple outlets\".", suggestion: "Name the outlets, or drop the claim." },
495
+ "buzzword-phrase": { severity: "low", message: "A stock office phrase: \"harness the power of\", \"at the forefront of\".", suggestion: "Say what it can actually do." },
496
+ "faux-insight": { severity: "low", message: "A line promising a secret: \"here's what nobody tells you\".", suggestion: "Show the point. Do not announce it." },
497
+ "rhetorical-qa": { severity: "low", message: "The \"The result? X.\" trick keeps coming back.", suggestion: "Use it once at most." },
498
+ "didactic-note": { severity: "low", message: "A teacherly disclaimer: \"it's important to understand\", \"results may vary\".", suggestion: "Cut it, or make it specific." },
499
+ "narrative-cliche": { severity: "low", message: "A worn-out story phrase: \"faced numerous challenges\", \"a poignant reminder\".", suggestion: "Say what actually happened." },
500
+ "valuable-insights": { severity: "low", message: "A stock academic filler phrase: \"provides valuable insights into\".", suggestion: "State the insight itself." },
501
+ "copula-avoidance": { severity: "low", message: "The text keeps dodging plain \"is\" and \"has\" in favour of \"serves as\", \"stands as\" and \"functions as\". " + FORMAL, suggestion: "Use \"is\" and \"has\" where they fit." },
502
+ "bold-label-bullets": { severity: "low", message: "A run of bullets all shaped \"**Label:** description\". Technical documents use this shape too.", suggestion: "Turn it into sentences, or vary the shape of the items." },
503
+ "emoji-decoration": { severity: "low", message: "Emoji are used on headings or bullets. That is a chat-window habit in a piece of business writing.", suggestion: "Take the decorative emoji out of this kind of writing." },
504
+ "heading-inflation": { severity: "low", message: "There are a lot of headings for the amount of writing under them. SEO advice produces the same shape.", suggestion: "Merge sections whose body is only a sentence or two." },
505
+ "staccato-fragments": { severity: "low", message: "A run of very short, punchy fragments. Ad copy does this on purpose too.", suggestion: "Join some of them into full sentences." },
506
+ "tricolon-density": { severity: "low", message: "Three-part lists such as \"faster, cheaper, simpler\" turn up a lot for a piece this long.", suggestion: "Break the pattern: use two items, or four." },
507
+ "transition-stacking": { severity: "low", message: "Most paragraphs open on a formal joining word: \"Furthermore\", \"Moreover\", \"Additionally\". " + FORMAL, suggestion: "Let the content do the joining in most paragraphs." },
508
+ "quote-inconsistency": { severity: "low", message: "Curly and straight quotation marks are mixed together. That usually comes from pasting out of a chat window, though word processors do it too.", suggestion: "Make the quotation marks match, either way round." },
509
+ "token-cutoff": { severity: "low", message: "The text stops in the middle of a sentence, the shape of an answer that ran out of room, or a paste that went wrong.", suggestion: "Finish the last sentence, or delete it." },
510
+ "setup-expansion-cadence": { severity: "low", message: "Short sentence, then long one, over and over, or the other way round. " + FORMAL, suggestion: "Keep the pattern only where the short sentence works on its own." },
511
+ "passive-ratio": { severity: "low", message: "A lot of the sentences hide who did the thing: \"mistakes were made\". That is high for a blog or a marketing piece. Academic writing does it constantly. " + FORMAL, suggestion: "Rewrite most sentences so the person or thing doing it comes first." },
512
+ "low-specificity": { severity: "low", message: "Almost no numbers, dates or names for a piece this long. Corporate writers produce empty writing too.", suggestion: "Add facts a reader could go and check." },
513
+ "adjacent-lemma-repeat": { severity: "low", message: "Neighbouring sentences keep reusing the same word. " + FORMAL, suggestion: "Merge the repetitive sentences, or change the wording where it reads naturally." },
514
+ "fiction-claudeism": { severity: "low", message: "Phrases that turn up a lot in Claude's fiction writing. Romance authors use several of them quite normally.", suggestion: "Put the stock phrases into your own words." },
515
+ "fiction-promptonym": { severity: "low", message: "A character name that AI writing produces far more often than people do. \"Elara Voss\" is the best-known one.", suggestion: "Pick a less loaded name if you want one." },
516
+ "fiction-slop-phrase": { severity: "low", message: "Several well-worn fiction lines land in the same piece. All of them were human clichés long before AI.", suggestion: "Cut or rework the stock moments." },
517
+ "owner-phrase-b": { severity: "low", message: "A phrase from the second generic-writing phrasebook.", suggestion: "Replace it with something specific." },
518
+ "owner-vocab-b": { severity: "low", message: "Several second-string filler words sit close together: \"essence\", \"facet\", \"pesky\", \"folks\".", suggestion: "Swap them for plainer words where they add nothing." },
519
+ "directive-colon-bullets": { severity: "low", message: "Several list items open on an order and a colon: \"Ensure X:\", \"Optimise Y:\". Real technical checklists look like this too.", suggestion: "Vary the way the items are built." },
520
+ "teach-preach-headings": { severity: "low", message: "Stock tutorial headings hold the piece together: \"Why it matters\", \"Final thoughts\", \"Key takeaways\".", suggestion: "Name each section after what is actually in it." },
521
+ "by-ving-template": { severity: "low", message: "The \"By doing X, you can Y\" shape keeps coming back.", suggestion: "Say the benefit straight out most of the time." },
522
+ "invalid-isbn": { severity: "low", message: "An ISBN in the text does not add up. Made-up references often fail this check, and so do typos.", suggestion: "Check the reference against the real book." },
523
+ "proximity-cluster": { severity: "low", message: "The same flagged buzzword comes back within a sentence or two.", suggestion: "Keep one use at most in each passage." },
524
+ // 2026.08.6 chat-formatting rules. Each one states the paste caveat: the
525
+ // check can only see formatting that survived the paste, so its ABSENCE says
526
+ // nothing about who wrote the text.
527
+ "markdown-bold": { severity: "low", message: "Raw **bold** markdown is showing in the text. None of the 169 human-written documents we checked had it. " + PASTE, suggestion: "Delete the stars, or apply real bold." },
528
+ "markdown-heading": { severity: "low", message: "A raw markdown heading line, the kind that starts with # signs, is showing in the text. None of the 169 human-written documents we checked had one. " + PASTE, suggestion: "Turn it into a real heading, or delete it." },
529
+ "markdown-furniture": { severity: "low", message: "Chat-window formatting shapes this text: runs of bold, heading lines, or a wall of bullets. None of the 169 human-written documents we checked had that combination. " + PASTE, suggestion: "Rebuild the formatting properly for wherever this is going." },
530
+ };
531
+ /**
532
+ * Harvest tells deliberately NOT implemented as user-facing rules. Each entry
533
+ * names the seed/owner id and the reason (research tier C, correction, gate
534
+ * the engine cannot provide, or licensing). Kept exported so coverage of the
535
+ * research harvest is auditable in code.
536
+ */
537
+ export const EXCLUDED_TELLS = [
538
+ { id: "lex-magic-adverbs", reason: "Tier C: feature-writing staple (quietly/deeply/fundamentally); too common in human prose to flag." },
539
+ { id: "lex-clinical-formality", reason: "Tier C: standard register in government/legal/academic prose; ESL writers are taught this vocabulary." },
540
+ { id: "lex-latinate-shift", reason: "Tier C: formal register and ESL academic training produce the same corpus shift; needs baselines the rule engine lacks." },
541
+ { id: "lex-concrete-overuse", reason: "Tier C: ordinary word; only notable at density in platform-specific defensive replies." },
542
+ { id: "lex-crosslingual-34", reason: "Tier C: non-English module; per-language calibration and time decay required before any flag ships." },
543
+ { id: "lex-multilingual-translationese", reason: "Tier C: overlaps native bureaucratic registers that predate AI; held for a non-English module." },
544
+ { id: "phr-in-connection-with", reason: "Tier C: standard legal/police-report phrasing; only iterative abundance signals." },
545
+ { id: "phr-false-ranges", reason: "Tier C: separating fake from real 'from X to Y' ranges needs semantics, not regex." },
546
+ { id: "phr-colon-reveal", reason: "Tier C: genuinely good device; recurrence-only signal with heavy human-copywriter overlap." },
547
+ { id: "phr-audience-bracketing", reason: "Tier C in the seed for the generic form; the engine keeps only its pre-existing template-phrase variant." },
548
+ { id: "phr-analogy-template", reason: "Tier C: explainer journalism uses 'Think of X as the Y of Z' constantly and well; needs semantic judgement." },
549
+ { id: "phr-paired-adjectives", reason: "Tier C: legal drafting doubles synonyms deliberately; common human filler." },
550
+ { id: "phr-weak-verbs", reason: "Tier C: 'is designed to' is accurate when describing genuine design intent." },
551
+ { id: "phr-flip-script", reason: "Tier C: sports and culture journalists genuinely say it." },
552
+ { id: "phr-refers-to", reason: "Tier C: textbook glossaries legitimately use 'refers to'." },
553
+ { id: "phr-fake-profound-kicker", reason: "Tier C: personal essayists earn the aphoristic closer; detecting the unearned variant needs semantics." },
554
+ { id: "phr-conservation-boilerplate", reason: "Tier C: niche domain; conservationists write about status legitimately." },
555
+ { id: "phr-x-rather-than-y", reason: "Tier C: completely standard English; only meaningful at very high density (Grok attribution hint recorded in docs)." },
556
+ { id: "phr-ultimate-guide", reason: "Tier C: human SEO writers used 'ultimate guide' titles since ~2010." },
557
+ { id: "art-stale-access-dates", reason: "Tier C: copied citations, offline drafting and batch merges confound." },
558
+ { id: "pun-oxford-comma", reason: "Tier C / anti-tell: house style for many publishers; corpus-drift indicator only, never per-document." },
559
+ { id: "pun-colon-titles", reason: "Tier C: academic titles used colons for decades; anecdotal evidence base." },
560
+ { id: "pun-human-marker-deficit", reason: "Tier C: absence-of-punctuation features are weak alone; plain-style human writers avoid semicolons deliberately." },
561
+ { id: "str-nominalisation", reason: "Tier C: needs POS tagging and per-register calibration; bureaucratic/legal prose is naturally nominalisation-heavy." },
562
+ { id: "str-title-case-headings", reason: "Tier C as a style-mismatch rule (needs house-style config); the engine's pre-existing register-gated title-case rule is retained unchanged." },
563
+ { id: "str-compound-headings", reason: "Tier C: humans write 'Awards and recognition' sections; only exact canned wordings at corpus scale signal." },
564
+ { id: "str-fractal-summaries", reason: "Tier C: essay pedagogy teaches 'tell them what you told them'; needs semantic detection." },
565
+ { id: "str-anaphora", reason: "Tier C: classical rhetoric and speechwriting staple." },
566
+ { id: "str-question-headings", reason: "Tier C + binding correction: deliberately taught SEO/AEO practice — human-authored AEO content trips this; needs a genre prior. Not implemented." },
567
+ { id: "str-uniform-faq", reason: "Tier C + binding correction: SEO guides teach humans this exact snippet template; the pre-existing uniform-sections measurement stays corroboration-weight only and is capped with the other stylometrics." },
568
+ { id: "str-unnecessary-tables", reason: "Tier C: 'trivial content tabulated' needs judgement; data journalists use tables well." },
569
+ { id: "str-deletion-test", reason: "Tier C: highest-FP category; could ship as a QUALITY metric someday, never as an authenticity flag." },
570
+ { id: "str-citation-absence", reason: "Tier C: genre-dependent absence signal; short or informal human texts also skip citations." },
571
+ { id: "str-elegant-variation", reason: "Tier C: needs coreference resolution; old-school journalism taught the same habit." },
572
+ { id: "str-genre-glitch", reason: "Tier C: needs register classification; feature journalists write deliberate colour." },
573
+ { id: "str-balanced-noncommitment", reason: "Tier C: diplomatic and academic hedging is human too." },
574
+ { id: "str-manufactured-personality", reason: "Tier C frontier: fake casual dev-blog voice defeats wordlists; needs semantic/behavioural detection." },
575
+ { id: "str-invented-labels", reason: "Tier C: academics coin terms legitimately with definition and citation." },
576
+ { id: "str-false-agency", reason: "Tier C: metonymy is standard English ('the market rallied'); cluster corroborator only." },
577
+ { id: "str-edit-summary-formulas", reason: "Tier C: platform-specific (wiki edit summaries); future surface for commits/CMS revision notes, not the core engine." },
578
+ { id: "str-defensive-comments", reason: "Tier C: platform-specific comment repertoire; polite non-native speakers write formally." },
579
+ { id: "str-hallucinated-conventions", reason: "Tier C: requires a platform knowledge base; new users make similar mistakes by hand." },
580
+ { id: "str-essay-scaffold", reason: "Tier B in the seed but needs genre awareness the engine lacks; its measurable parts ship via heading-inflation, transition-stacking and the pre-existing uniformity rules." },
581
+ { id: "str-markdown-leakage-full", reason: "Tier A only INSIDE a surface gate the core engine does not have (markdown-native vs plain-text venue). Bold/heading density rules pre-exist; only the always-safe escaped-literal subset ships (escaped-markup-literal). Full rule awaits the surface concept in the WordPress layer." },
582
+ { id: "sty-low-perplexity", reason: "Binding exclusion: >50% non-native TOEFL false-positive rate; legal exposure; out of scope for a rule engine." },
583
+ { id: "sty-ttr-paradox", reason: "Tier C: contradictory directions by register; existing low-ttr rule stays low-weight instead." },
584
+ { id: "sty-hedging-reversal", reason: "Binding correction: hedges DECREASED in post-LLM text — a naive hedging rule fires backwards and is deliberately not built. The pre-existing hedge-stack rule targets the specific modal+adverb stack, not hedge frequency." },
585
+ { id: "sty-positivity-skew", reason: "Tier C: PR and brand copy is deliberately positive; lazy human reviewers write the same praise sandwich." },
586
+ { id: "sty-homogenisation", reason: "Tier C: corpus-level (multi-document) feature; not a per-document rule." },
587
+ { id: "sty-style-shift", reason: "Tier C: needs per-author history; future multi-document feature." },
588
+ { id: "sty-machine-cleanliness", reason: "Anti-tell: Grammarly makes human text machine-clean; penalising cleanliness smears good writers. Never scored." },
589
+ { id: "sty-anti-tells", reason: "The false-positive spec: perfect grammar, 'robotic feel', formality, isolated transitions, Oxford commas and em-dash presence are documented INEFFECTIVE indicators — encoded here as exclusions and exercised by QA fixtures." },
590
+ { id: "sty-human-whitelist", reason: "Inverse signals (typos, first-person anecdote, pre-Nov-2022 dates) belong to a future negative-weight/protect-list layer, not to positive flags; flagging their absence would cross BRIEF.md §5's claim boundary." },
591
+ { id: "sty-first-words", reason: "Unverified methodology and unlicensed source repo; not copied, not implemented." },
592
+ { id: "mod-grok-idiolect", reason: "Tier C: social scientists use the vocabulary natively; attribution hint recorded in docs only." },
593
+ { id: "mod-vendor-accents", reason: "Tier C: ML-classifier roadmap item (BRIEF §21 Tier C model), not a rule." },
594
+ { id: "mod-structural-dialects", reason: "Tier C: vendor-blog evidence; used for calibration expectations, not scoring." },
595
+ { id: "mod-rlhf-root-cause", reason: "Engine-design finding, not a surface tell." },
596
+ { id: "art-fabricated-refs-network", reason: "Dead-link/DOI checks need network access the offline engine forbids; only the offline ISBN checksum ships (invalid-isbn)." },
597
+ { id: "owner-journey-bare", reason: "Owner list, high FP: bare 'journey' needs a user-journey/travel guard the rule layer cannot express reliably; only phrase forms ship." },
598
+ { id: "owner-tier-c-vocab", reason: "Owner tier C (thus, ultimately, nonetheless, promptly, remnant, arena, era, cool, essential, substantially, on the other hand, world of, competitive advantages, break down, stepping into): very high FP ordinary English; documented, not flagged." },
599
+ { id: "owner-shed-article-fragments", reason: "Topic-specific residue from one AI article (caulk/weatherstripping etc.); not generic tells." },
600
+ { id: "owner-absence-of-humanity", reason: "Zero-first-person, no-typos, no-emphasis-irregularity: flagging the absence of human traits crosses BRIEF.md §5 from editorial evidence toward authorship inference. Documented only." },
601
+ { id: "owner-code-comment-tells", reason: "Owner tier C: only relevant if the engine ever scans code identifiers/comments." },
602
+ { id: "owner-meta-description-template", reason: "Owner tier B but requires the WordPress meta-field surface; not implementable on plain text." },
603
+ { id: "owner-cross-page-templates", reason: "Owner D2 §3.1 cross-page comparison is site-level; out of scope for the single-document engine." },
604
+ { id: "lex-kobak-style-407-full", reason: "The full 407-word excess_words.csv is not bundled (licence file unverified in-repo); a representative regex subset ships as kobak-density." },
605
+ // ── 2026.08.6: provider-eval risk-tiered candidates (PROVIDER-EVAL-2026-08.md
606
+ // §4.2) — measured NONZERO human false positives on the 169-human corpus.
607
+ // Deliberately NOT implemented; each needs an explicit owner decision.
608
+ { id: "pe-tier3-threshold-lowering", reason: "Provider-eval §4.2/§4.3: lowering the tier3 threshold (0.8555/0.85/0.80) buys 33.7-75.7% TPR at 1-7 measured human FPs, dominated by business-marketing prose. ML workstream's recalibration problem, not a rules change; do not ship without owner decision." },
609
+ { id: "pe-emdash-p99-6.59", reason: "Provider-eval §4.2: em-dash > 6.59/1000 (human p99) flags 2/169 humans (1 business-marketing). Em-dash rules already carry a documented human-FP history; owner decision required." },
610
+ { id: "pe-fragmentshare-p99", reason: "Provider-eval §4.2: fragmentShare > 0.319 flags 2/169 humans. Business-marketing humans are themselves choppy; owner decision required." },
611
+ { id: "pe-shortsentshare-p99", reason: "Provider-eval §4.2: shortSentShare > 0.535 flags 2/169 humans (1 business-marketing); owner decision required." },
612
+ { id: "pe-flatline-escalation", reason: "Provider-eval §4.2: sentence-flatline with >=3 findings => mixed_signals flags 1/169 humans (qa-finance); owner decision required." },
613
+ { id: "pe-emdash-extreme-10.07", reason: "Provider-eval §4.1 footnote: em-dash > 10.07/1000 (the human maximum) measured zero-FP but yields only openai-25 18.0 / grok 12.0 with all other slices <=10.7, is GPT/Grok-attributing rather than AI-detecting, and sits one essayist beyond the measured human max. Held back with the risk tier pending owner decision; the five headline zero-FP changes shipped instead." },
614
+ ];