slopbrick 0.18.7 → 0.19.0

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.
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ var VERSION;
19
19
  var init_header = __esm({
20
20
  "src/types/_header.ts"() {
21
21
  "use strict";
22
- VERSION = "0.18.7";
22
+ VERSION = "0.19.0";
23
23
  }
24
24
  });
25
25
 
@@ -28220,6 +28220,20 @@ var init_unreachable = __esm({
28220
28220
  });
28221
28221
 
28222
28222
  // src/rules/dead/unused-import.ts
28223
+ function collectRustReferencedNames(source) {
28224
+ const out = /* @__PURE__ */ new Set();
28225
+ for (const m of source.matchAll(/\b([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
28226
+ out.add(m[1]);
28227
+ }
28228
+ return out;
28229
+ }
28230
+ function stripUseDeclarations(source) {
28231
+ let out = source;
28232
+ out = out.replace(/^\s*use\s+[\s\S]*?;\s*$/gm, "");
28233
+ out = out.replace(/\/\/[^\n]*/g, "");
28234
+ out = out.replace(/\/\*[\s\S]*?\*\//g, "");
28235
+ return out;
28236
+ }
28223
28237
  var unusedImportRule;
28224
28238
  var init_unused_import = __esm({
28225
28239
  "src/rules/dead/unused-import.ts"() {
@@ -28255,6 +28269,26 @@ var init_unused_import = __esm({
28255
28269
  advice: `Remove the import or use '${binding.name}' somewhere in the file. This is the most common AI-iteration rot \u2014 the model added the import when it introduced a feature, then rewrote the function without cleaning up.`
28256
28270
  });
28257
28271
  }
28272
+ if (facts.v2.rustFile) {
28273
+ const strippedSource = stripUseDeclarations(facts.v2._source ?? "");
28274
+ const referenced = collectRustReferencedNames(strippedSource);
28275
+ for (const imp of facts.v2.rustFile.imports) {
28276
+ for (const nameEntry of imp.names) {
28277
+ if (referenced.has(nameEntry.name)) continue;
28278
+ const source = ` from '${imp.path}'`;
28279
+ issues.push({
28280
+ ruleId: "dead/unused-import",
28281
+ category: "logic",
28282
+ severity: "low",
28283
+ aiSpecific: true,
28284
+ message: `Unused import: '${nameEntry.name}'${source}`,
28285
+ line: imp.line,
28286
+ column: imp.column,
28287
+ advice: `Remove the '${imp.path}' import or use '${nameEntry.name}' somewhere in the file. Rust's compiler only flags unused imports per module \u2014 the tree-sitter-backed walker here surfaces them for slopbrick's dead-code rules regardless of #[allow(unused_imports)].`
28288
+ });
28289
+ }
28290
+ }
28291
+ }
28258
28292
  return issues;
28259
28293
  }
28260
28294
  });
@@ -28997,10 +29031,67 @@ var init_dart = __esm({
28997
29031
  }
28998
29032
  });
28999
29033
 
29034
+ // src/engine/parser-rust.ts
29035
+ import Parser from "tree-sitter";
29036
+ import Rust from "tree-sitter-rust";
29037
+ function getRustParser() {
29038
+ if (cachedParser) return { ok: true, parser: cachedParser };
29039
+ if (loadError) return { ok: false, error: loadError };
29040
+ try {
29041
+ rustLanguage = Rust;
29042
+ if (!rustLanguage || typeof rustLanguage !== "object") {
29043
+ throw new Error("tree-sitter-rust: language export is missing or malformed");
29044
+ }
29045
+ const parser = new Parser();
29046
+ parser.setLanguage(rustLanguage);
29047
+ cachedParser = parser;
29048
+ return { ok: true, parser };
29049
+ } catch (err) {
29050
+ loadError = err instanceof Error ? err : new Error(String(err));
29051
+ return { ok: false, error: loadError };
29052
+ }
29053
+ }
29054
+ function effectiveParser() {
29055
+ if (forcedFailure) return { ok: false, error: forcedFailure };
29056
+ return getRustParser();
29057
+ }
29058
+ function parseRust(source) {
29059
+ const result = effectiveParser();
29060
+ if (!result.ok) return null;
29061
+ if (!source || source.trim() === "") return null;
29062
+ try {
29063
+ const rawTree = result.parser.parse(source);
29064
+ if (!rawTree) return null;
29065
+ const tree = rawTree;
29066
+ if (!tree || !tree.rootNode) return null;
29067
+ if (tree.rootNode.type === "ERROR" && tree.rootNode.childCount === 0) {
29068
+ return null;
29069
+ }
29070
+ return tree;
29071
+ } catch {
29072
+ return null;
29073
+ }
29074
+ }
29075
+ function isRustParserAvailable() {
29076
+ const result = getRustParser();
29077
+ return result.ok;
29078
+ }
29079
+ var cachedParser, rustLanguage, loadError, forcedFailure;
29080
+ var init_parser_rust = __esm({
29081
+ "src/engine/parser-rust.ts"() {
29082
+ "use strict";
29083
+ cachedParser = null;
29084
+ rustLanguage = null;
29085
+ loadError = null;
29086
+ forcedFailure = null;
29087
+ }
29088
+ });
29089
+
29000
29090
  // src/engine/visitors/rust.ts
29001
29091
  var rust_exports = {};
29002
29092
  __export(rust_exports, {
29003
- extractRustPatterns: () => extractRustPatterns
29093
+ extractRustPatterns: () => extractRustPatterns,
29094
+ parseRustFile: () => parseRustFile
29004
29095
  });
29005
29096
  function extractRustPatterns(filePath, source) {
29006
29097
  const service = [];
@@ -29053,188 +29144,487 @@ function extractRustPatterns(filePath, source) {
29053
29144
  }
29054
29145
  return { service, route, ormModel };
29055
29146
  }
29056
- var SERVICE_SUFFIXES4, SERVICE_SUFFIX_GROUP4, RUST_SERVICE_STRUCT_RE, RUST_SERVICE_IMPL_RE, RUST_ACTIX_ROUTE_RE, RUST_AXUM_ROUTE_RE, RUST_DIESEL_RE, RUST_SEAORM_RE, RUST_SQLX_RE;
29057
- var init_rust = __esm({
29058
- "src/engine/visitors/rust.ts"() {
29059
- "use strict";
29060
- SERVICE_SUFFIXES4 = [
29061
- "Service",
29062
- "Manager",
29063
- "Handler",
29064
- "Repository",
29065
- "Controller",
29066
- "Helper",
29067
- "Factory",
29068
- "Provider",
29069
- "Store",
29070
- "API",
29071
- "Client",
29072
- "Adapter",
29073
- "Resolver",
29074
- "Mapper",
29075
- "Transformer",
29076
- "Serializer",
29077
- "Validator",
29078
- "Strategy",
29079
- "Facade",
29080
- "Decorator",
29081
- "Observer",
29082
- "Builder",
29083
- "Command",
29084
- "Processor",
29085
- "Worker",
29086
- "Job",
29087
- "Actor",
29088
- "Executor"
29089
- ];
29090
- SERVICE_SUFFIX_GROUP4 = `(?:${SERVICE_SUFFIXES4.join("|")})`;
29091
- RUST_SERVICE_STRUCT_RE = new RegExp(
29092
- `^(?:pub(?:\\(crate\\)|\\(super\\))?\\s+)?struct\\s+(\\w+?)${SERVICE_SUFFIX_GROUP4}?\\b`,
29093
- "gm"
29094
- );
29095
- RUST_SERVICE_IMPL_RE = new RegExp(
29096
- `^impl(?:<[^>]+>)?\\s+(?:${SERVICE_SUFFIX_GROUP4}\\s+for\\s+)?(\\w+?)${SERVICE_SUFFIX_GROUP4}?\\b`,
29097
- "gm"
29098
- );
29099
- RUST_ACTIX_ROUTE_RE = /#\[(?:get|post|put|delete|patch|head|route)\(\s*"(\/[^"]+)"\s*[\),]/g;
29100
- RUST_AXUM_ROUTE_RE = /\.route\(\s*"(\/[^"]+)"\s*,\s*(?:get|post|put|delete|patch)/g;
29101
- RUST_DIESEL_RE = /#\[derive\([^\]]*Queryable[^\]]*\)\][\s\S]{0,200}?struct\s+(\w+)/g;
29102
- RUST_SEAORM_RE = /#\[derive\([^\]]*DeriveEntityModel[^\]]*\)\][\s\S]{0,200}?struct\s+(\w+)/g;
29103
- RUST_SQLX_RE = /#\[derive\([^\]]*sqlx::FromRow[^\]]*\)\][\s\S]{0,200}?struct\s+(\w+)/g;
29147
+ function parseRustFile(filePath, source, options = {}) {
29148
+ const empty = {
29149
+ imports: [],
29150
+ functions: [],
29151
+ structs: [],
29152
+ traits: [],
29153
+ impls: []
29154
+ };
29155
+ if (!isRustParserAvailable() || options.forceFallback) {
29156
+ return empty;
29104
29157
  }
29105
- });
29106
-
29107
- // src/engine/visitors/cpp.ts
29108
- var cpp_exports = {};
29109
- __export(cpp_exports, {
29110
- extractCppPatterns: () => extractCppPatterns
29111
- });
29112
- function extractCppPatterns(filePath, source) {
29113
- const service = [];
29114
- const route = [];
29115
- const ormModel = [];
29116
- const seenService = /* @__PURE__ */ new Set();
29117
- for (const m of source.matchAll(CPP_SERVICE_RE)) {
29118
- const name = m[1];
29119
- if (seenService.has(name)) continue;
29120
- seenService.add(name);
29121
- service.push({ name, files: [filePath], imports: [] });
29158
+ const tree = parseRust(source);
29159
+ if (!tree) return empty;
29160
+ return walkRustTree(tree);
29161
+ }
29162
+ function walkRustTree(tree) {
29163
+ const ctx = { inTestConfig: false };
29164
+ const out = {
29165
+ imports: [],
29166
+ functions: [],
29167
+ structs: [],
29168
+ traits: [],
29169
+ impls: []
29170
+ };
29171
+ walkChildren(tree.rootNode, ctx, out);
29172
+ return out;
29173
+ }
29174
+ function walkChildren(node, ctx, out) {
29175
+ for (let i = 0; i < node.namedChildCount; i++) {
29176
+ const child = node.namedChild(i);
29177
+ if (!child) continue;
29178
+ visitNode(child, ctx, out);
29122
29179
  }
29123
- const seenRoute = /* @__PURE__ */ new Set();
29124
- for (const m of source.matchAll(CPP_CROW_ROUTE_RE)) {
29125
- const name = m[1];
29126
- if (seenRoute.has(name)) continue;
29127
- seenRoute.add(name);
29128
- route.push({ name, files: [filePath], imports: [] });
29180
+ }
29181
+ function visitNode(node, ctx, out) {
29182
+ const innerCtx = { ...ctx };
29183
+ const attrState = readPrecedingAttributes(node);
29184
+ switch (node.type) {
29185
+ case "use_declaration": {
29186
+ out.imports.push(extractUse(node));
29187
+ return;
29188
+ }
29189
+ case "function_item": {
29190
+ const isMethod = isInImplBlock(node);
29191
+ out.functions.push(extractFunction(node, attrState, innerCtx.inTestConfig, isMethod));
29192
+ walkChildren(getField(node, "body") ?? node, innerCtx, out);
29193
+ return;
29194
+ }
29195
+ case "struct_item": {
29196
+ out.structs.push(extractStruct(node, attrState));
29197
+ const body = getField(node, "body");
29198
+ if (body) walkChildren(body, innerCtx, out);
29199
+ return;
29200
+ }
29201
+ case "trait_item": {
29202
+ out.traits.push(extractTrait(node, attrState));
29203
+ const body = getField(node, "body");
29204
+ if (body) walkChildren(body, innerCtx, out);
29205
+ return;
29206
+ }
29207
+ case "impl_item": {
29208
+ const implEntry = extractImpl(node, attrState, innerCtx);
29209
+ out.impls.push(implEntry.entry);
29210
+ const body = getField(node, "body");
29211
+ if (body) walkChildren(body, innerCtx, out);
29212
+ return;
29213
+ }
29214
+ case "mod_item": {
29215
+ const modIsTest = innerCtx.inTestConfig || attrState.isTestCfg;
29216
+ const modCtx = { ...innerCtx, inTestConfig: modIsTest };
29217
+ const body = getField(node, "body");
29218
+ if (body) walkChildren(body, modCtx, out);
29219
+ return;
29220
+ }
29221
+ default: {
29222
+ for (let i = 0; i < node.namedChildCount; i++) {
29223
+ const child = node.namedChild(i);
29224
+ if (child) visitNode(child, innerCtx, out);
29225
+ }
29226
+ }
29129
29227
  }
29130
- for (const m of source.matchAll(CPP_DROGON_ROUTE_RE)) {
29131
- const name = m[1] || m[2];
29228
+ }
29229
+ function readPrecedingAttributes(node) {
29230
+ const state = {
29231
+ isTestCfg: false,
29232
+ isTest: false,
29233
+ isPub: false,
29234
+ derives: []
29235
+ };
29236
+ const parent = node.parent;
29237
+ if (!parent) return state;
29238
+ for (let i = 0; i < parent.namedChildCount; i++) {
29239
+ const sibling = parent.namedChild(i);
29240
+ if (!sibling || sibling === node) continue;
29241
+ if (sibling.endIndex > node.startIndex) continue;
29242
+ if (sibling.type !== "attribute_item") continue;
29243
+ decodeAttribute(sibling, state);
29244
+ }
29245
+ return state;
29246
+ }
29247
+ function decodeAttribute(attr, state) {
29248
+ for (let i = 0; i < attr.namedChildCount; i++) {
29249
+ const child = attr.namedChild(i);
29250
+ if (!child || child.type !== "attribute") continue;
29251
+ const name = firstIdentifier(child);
29132
29252
  if (!name) continue;
29133
- if (seenRoute.has(name)) continue;
29134
- seenRoute.add(name);
29135
- route.push({ name, files: [filePath], imports: [] });
29136
- }
29137
- for (const m of source.matchAll(CPP_PISTACHE_ROUTE_RE)) {
29138
- const name = m[1];
29139
- if (seenRoute.has(name)) continue;
29140
- seenRoute.add(name);
29141
- route.push({ name, files: [filePath], imports: [] });
29253
+ if (name === "cfg") {
29254
+ const cfgText = child.text;
29255
+ if (/\btest\b/.test(cfgText) && !/\bnot\(test\)/.test(cfgText)) {
29256
+ state.isTestCfg = true;
29257
+ }
29258
+ } else if (name === "test") {
29259
+ state.isTest = true;
29260
+ } else if (name === "derive") {
29261
+ for (let j = 0; j < child.namedChildCount; j++) {
29262
+ const inner = child.namedChild(j);
29263
+ if (inner) {
29264
+ const matches = inner.text.matchAll(/\b([A-Z][A-Za-z0-9_]*)\b/g);
29265
+ for (const m of matches) state.derives.push(m[1]);
29266
+ }
29267
+ }
29268
+ }
29142
29269
  }
29143
- return { service, route, ormModel };
29144
29270
  }
29145
- var SERVICE_SUFFIXES5, SERVICE_SUFFIX_GROUP5, CPP_SERVICE_RE, CPP_CROW_ROUTE_RE, CPP_DROGON_ROUTE_RE, CPP_PISTACHE_ROUTE_RE;
29146
- var init_cpp = __esm({
29147
- "src/engine/visitors/cpp.ts"() {
29148
- "use strict";
29149
- SERVICE_SUFFIXES5 = [
29150
- "Service",
29151
- "Manager",
29152
- "Handler",
29153
- "Repository",
29154
- "Controller",
29155
- "Helper",
29156
- "Factory",
29157
- "Provider",
29158
- "Store",
29159
- "API",
29160
- "Client",
29161
- "Adapter",
29162
- "Resolver",
29163
- "Mapper",
29164
- "Transformer",
29165
- "Serializer",
29166
- "Validator",
29167
- "Strategy",
29168
- "Facade",
29169
- "Decorator",
29170
- "Observer",
29171
- "Builder",
29172
- "Command",
29173
- "Processor",
29174
- "Worker",
29175
- "Job"
29176
- ];
29177
- SERVICE_SUFFIX_GROUP5 = `(?:${SERVICE_SUFFIXES5.join("|")})`;
29178
- CPP_SERVICE_RE = new RegExp(
29179
- `^(?:public\\s+|private\\s+|protected\\s+|virtual\\s+|abstract\\s+|static\\s+|inline\\s+|constexpr\\s+|explicit\\s+|final\\s+|extern\\s+)*(?:class|struct)\\s+(\\w+?)${SERVICE_SUFFIX_GROUP5}?\\b`,
29180
- "gm"
29181
- );
29182
- CPP_CROW_ROUTE_RE = /CROW_ROUTE\s*\(\s*\w+\s*,\s*"(\/[^"]+)"/g;
29183
- CPP_DROGON_ROUTE_RE = /\.registerHandler\(\s*"(\/[^"]+)"[\s,)]|\.\b(?:get|post|put|delete|patch)\(\s*"(\/[^"]+)"/g;
29184
- CPP_PISTACHE_ROUTE_RE = /Routes::(?:get|post|put|delete|patch)\s*\(\s*[^,]+,\s*"(\/[^"]+)"/g;
29271
+ function firstIdentifier(node) {
29272
+ const text = node.text;
29273
+ const idMatch = text.match(/^([a-zA-Z_][a-zA-Z0-9_]*)/);
29274
+ return idMatch ? idMatch[1] : null;
29275
+ }
29276
+ function extractUse(node) {
29277
+ const text = node.text;
29278
+ const argument = node.namedChild(0);
29279
+ const argField = node.childForFieldName("argument");
29280
+ const path = argField?.text ?? argument?.text ?? "";
29281
+ const names = [];
29282
+ let isGlob = false;
29283
+ const useListNode = findUseListNode(argument);
29284
+ if (useListNode) {
29285
+ for (let i = 0; i < useListNode.namedChildCount; i++) {
29286
+ const item = useListNode.namedChild(i);
29287
+ if (item.type === "use_wildcard") {
29288
+ isGlob = true;
29289
+ } else if (item.type === "identifier") {
29290
+ names.push({ name: item.text });
29291
+ } else if (item.type === "use_as_clause") {
29292
+ const alias = item.childForFieldName("alias");
29293
+ const binding = item.childForFieldName("path");
29294
+ if (binding) {
29295
+ names.push({ name: binding.text, alias: alias?.text });
29296
+ } else {
29297
+ names.push({ name: item.text });
29298
+ }
29299
+ } else if (item.type === "scoped_identifier") {
29300
+ const idents = collectIdentifiers(item);
29301
+ names.push({ name: idents[idents.length - 1] ?? item.text });
29302
+ } else if (item.text.includes(" as ")) {
29303
+ const [head, alias] = item.text.split(/\s+as\s+/);
29304
+ names.push({ name: (head ?? "").trim(), alias: alias?.trim() });
29305
+ } else {
29306
+ names.push({ name: item.text });
29307
+ }
29308
+ }
29309
+ } else if (argument?.type === "use_wildcard") {
29310
+ isGlob = true;
29311
+ } else if (argument) {
29312
+ const idents = collectIdentifiers(argument);
29313
+ const last = idents[idents.length - 1] ?? argument.text;
29314
+ names.push({ name: last });
29315
+ const asMatch = text.match(/\s+as\s+(\w+)\s*;?\s*$/);
29316
+ if (asMatch) names[0].alias = asMatch[1];
29185
29317
  }
29186
- });
29187
-
29188
- // src/engine/visitors/java.ts
29189
- var java_exports = {};
29190
- __export(java_exports, {
29191
- extractJavaPatterns: () => extractJavaPatterns
29192
- });
29193
- function extractJavaPatterns(filePath, source) {
29194
- const service = [];
29195
- const route = [];
29196
- const ormModel = [];
29197
- const seenService = /* @__PURE__ */ new Set();
29198
- for (const m of source.matchAll(JAVA_SERVICE_RE)) {
29199
- const name = m[1];
29200
- if (seenService.has(name)) continue;
29201
- seenService.add(name);
29202
- service.push({ name, files: [filePath], imports: [] });
29318
+ return {
29319
+ path: path.trim(),
29320
+ names,
29321
+ isGlob,
29322
+ line: node.startPosition.row + 1,
29323
+ column: node.startPosition.column
29324
+ };
29325
+ }
29326
+ function extractFunction(node, attrs, inheritedTestConfig, isMethod) {
29327
+ const nameNode = node.childForFieldName("name");
29328
+ const name = nameNode?.text ?? "<anon>";
29329
+ const params = node.childForFieldName("parameters");
29330
+ const body = node.childForFieldName("body");
29331
+ const visibilityMod = node.namedChild(0);
29332
+ const isPublic = visibilityMod?.type === "visibility_modifier";
29333
+ let receiver;
29334
+ if (isMethod && params) {
29335
+ const selfParam = findSelfParameter(params);
29336
+ if (selfParam) receiver = selfParam.text;
29337
+ }
29338
+ const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
29339
+ const inTestConfig = inheritedTestConfig || attrs.isTest || attrs.isTestCfg || isFunctionAttrTest(node);
29340
+ return {
29341
+ name,
29342
+ line: node.startPosition.row + 1,
29343
+ column: node.startPosition.column,
29344
+ isPublic,
29345
+ isMethod,
29346
+ receiver,
29347
+ bodyLines,
29348
+ inTestConfig
29349
+ };
29350
+ }
29351
+ function extractStruct(node, attrs) {
29352
+ const nameNode = node.childForFieldName("name");
29353
+ return {
29354
+ name: nameNode?.text ?? "<anon>",
29355
+ line: node.startPosition.row + 1,
29356
+ column: node.startPosition.column,
29357
+ isPublic: hasVisibility(node),
29358
+ isDerive: attrs.derives.length > 0,
29359
+ derives: [...attrs.derives]
29360
+ };
29361
+ }
29362
+ function extractTrait(node, _attrs) {
29363
+ const nameNode = node.childForFieldName("name");
29364
+ return {
29365
+ name: nameNode?.text ?? "<anon>",
29366
+ line: node.startPosition.row + 1,
29367
+ column: node.startPosition.column,
29368
+ isPublic: hasVisibility(node)
29369
+ };
29370
+ }
29371
+ function extractImpl(node, _attrs, _ctx) {
29372
+ const traitNode = node.childForFieldName("trait");
29373
+ const typeNode = node.childForFieldName("type");
29374
+ const typeText = typeNode?.text ?? "<anon>";
29375
+ const traitText = traitNode?.text;
29376
+ const body = node.childForFieldName("body");
29377
+ const methods = [];
29378
+ if (body) {
29379
+ for (let i = 0; i < body.namedChildCount; i++) {
29380
+ const child = body.namedChild(i);
29381
+ if (child?.type === "function_item") {
29382
+ const m = child.childForFieldName("name");
29383
+ if (m) methods.push(m.text);
29384
+ }
29385
+ }
29203
29386
  }
29204
- const seenRoute = /* @__PURE__ */ new Set();
29205
- for (const m of source.matchAll(JAVA_SPRING_ROUTE_RE)) {
29206
- const name = m[1];
29207
- if (seenRoute.has(name)) continue;
29208
- seenRoute.add(name);
29209
- route.push({ name, files: [filePath], imports: [] });
29387
+ return {
29388
+ entry: {
29389
+ type: typeText,
29390
+ trait: traitText ?? void 0,
29391
+ methods,
29392
+ line: node.startPosition.row + 1,
29393
+ column: node.startPosition.column
29394
+ }
29395
+ };
29396
+ }
29397
+ function findUseListNode(argument) {
29398
+ if (!argument) return null;
29399
+ if (argument.type === "use_list") return argument;
29400
+ if (argument.type === "scoped_use_list") {
29401
+ for (let i = 0; i < argument.namedChildCount; i++) {
29402
+ const c = argument.namedChild(i);
29403
+ if (c?.type === "use_list") return c;
29404
+ }
29210
29405
  }
29211
- for (const m of source.matchAll(JAVA_JAXRS_CLASS_PATH_RE)) {
29212
- const name = m[1];
29213
- if (name === "" || name === "/") continue;
29214
- if (seenRoute.has(name)) continue;
29215
- seenRoute.add(name);
29216
- route.push({ name, files: [filePath], imports: [] });
29406
+ return null;
29407
+ }
29408
+ function getField(node, fieldName) {
29409
+ return node.childForFieldName(fieldName);
29410
+ }
29411
+ function collectIdentifiers(node) {
29412
+ const out = [];
29413
+ for (let i = 0; i < node.namedChildCount; i++) {
29414
+ const child = node.namedChild(i);
29415
+ if (!child) continue;
29416
+ if (child.type === "identifier" || child.type === "type_identifier") {
29417
+ out.push(child.text);
29418
+ } else if (child.type === "scoped_identifier" || child.type === "nested_identifier") {
29419
+ out.push(...collectIdentifiers(child));
29420
+ }
29217
29421
  }
29218
- const seenOrm = /* @__PURE__ */ new Set();
29219
- for (const m of source.matchAll(JAVA_ORM_ANNOTATION_RE)) {
29220
- const name = m[1];
29221
- if (seenOrm.has(name)) continue;
29222
- seenOrm.add(name);
29223
- ormModel.push({ name, files: [filePath], imports: [] });
29422
+ return out;
29423
+ }
29424
+ function hasVisibility(node) {
29425
+ return node.namedChild(0)?.type === "visibility_modifier";
29426
+ }
29427
+ function isInImplBlock(node) {
29428
+ for (let p = node.parent; p; p = p.parent) {
29429
+ if (p.type === "impl_item") return true;
29430
+ if (p.type === "function_item" || p.type === "source_file") return false;
29224
29431
  }
29225
- for (const m of source.matchAll(JAVA_ORM_INHERIT_RE)) {
29226
- const name = m[1];
29227
- if (seenOrm.has(name)) continue;
29228
- seenOrm.add(name);
29229
- ormModel.push({ name, files: [filePath], imports: [] });
29432
+ return false;
29433
+ }
29434
+ function findSelfParameter(params) {
29435
+ for (let i = 0; i < params.namedChildCount; i++) {
29436
+ const child = params.namedChild(i);
29437
+ if (child?.type === "self_parameter") return child;
29230
29438
  }
29231
- return { service, route, ormModel };
29439
+ return null;
29232
29440
  }
29233
- var SERVICE_SUFFIXES6, SERVICE_SUFFIX_GROUP6, JAVA_SERVICE_RE, JAVA_SPRING_ROUTE_RE, JAVA_JAXRS_CLASS_PATH_RE, JAVA_ORM_ANNOTATION_RE, JAVA_ORM_INHERIT_RE;
29234
- var init_java = __esm({
29235
- "src/engine/visitors/java.ts"() {
29441
+ function isFunctionAttrTest(node) {
29442
+ void node;
29443
+ return false;
29444
+ }
29445
+ var SERVICE_SUFFIXES4, SERVICE_SUFFIX_GROUP4, RUST_SERVICE_STRUCT_RE, RUST_SERVICE_IMPL_RE, RUST_ACTIX_ROUTE_RE, RUST_AXUM_ROUTE_RE, RUST_DIESEL_RE, RUST_SEAORM_RE, RUST_SQLX_RE;
29446
+ var init_rust = __esm({
29447
+ "src/engine/visitors/rust.ts"() {
29236
29448
  "use strict";
29237
- SERVICE_SUFFIXES6 = [
29449
+ init_parser_rust();
29450
+ SERVICE_SUFFIXES4 = [
29451
+ "Service",
29452
+ "Manager",
29453
+ "Handler",
29454
+ "Repository",
29455
+ "Controller",
29456
+ "Helper",
29457
+ "Factory",
29458
+ "Provider",
29459
+ "Store",
29460
+ "API",
29461
+ "Client",
29462
+ "Adapter",
29463
+ "Resolver",
29464
+ "Mapper",
29465
+ "Transformer",
29466
+ "Serializer",
29467
+ "Validator",
29468
+ "Strategy",
29469
+ "Facade",
29470
+ "Decorator",
29471
+ "Observer",
29472
+ "Builder",
29473
+ "Command",
29474
+ "Processor",
29475
+ "Worker",
29476
+ "Job",
29477
+ "Actor",
29478
+ "Executor"
29479
+ ];
29480
+ SERVICE_SUFFIX_GROUP4 = `(?:${SERVICE_SUFFIXES4.join("|")})`;
29481
+ RUST_SERVICE_STRUCT_RE = new RegExp(
29482
+ `^(?:pub(?:\\(crate\\)|\\(super\\))?\\s+)?struct\\s+(\\w+?)${SERVICE_SUFFIX_GROUP4}?\\b`,
29483
+ "gm"
29484
+ );
29485
+ RUST_SERVICE_IMPL_RE = new RegExp(
29486
+ `^impl(?:<[^>]+>)?\\s+(?:${SERVICE_SUFFIX_GROUP4}\\s+for\\s+)?(\\w+?)${SERVICE_SUFFIX_GROUP4}?\\b`,
29487
+ "gm"
29488
+ );
29489
+ RUST_ACTIX_ROUTE_RE = /#\[(?:get|post|put|delete|patch|head|route)\(\s*"(\/[^"]+)"\s*[\),]/g;
29490
+ RUST_AXUM_ROUTE_RE = /\.route\(\s*"(\/[^"]+)"\s*,\s*(?:get|post|put|delete|patch)/g;
29491
+ RUST_DIESEL_RE = /#\[derive\([^\]]*Queryable[^\]]*\)\][\s\S]{0,200}?struct\s+(\w+)/g;
29492
+ RUST_SEAORM_RE = /#\[derive\([^\]]*DeriveEntityModel[^\]]*\)\][\s\S]{0,200}?struct\s+(\w+)/g;
29493
+ RUST_SQLX_RE = /#\[derive\([^\]]*sqlx::FromRow[^\]]*\)\][\s\S]{0,200}?struct\s+(\w+)/g;
29494
+ }
29495
+ });
29496
+
29497
+ // src/engine/visitors/cpp.ts
29498
+ var cpp_exports = {};
29499
+ __export(cpp_exports, {
29500
+ extractCppPatterns: () => extractCppPatterns
29501
+ });
29502
+ function extractCppPatterns(filePath, source) {
29503
+ const service = [];
29504
+ const route = [];
29505
+ const ormModel = [];
29506
+ const seenService = /* @__PURE__ */ new Set();
29507
+ for (const m of source.matchAll(CPP_SERVICE_RE)) {
29508
+ const name = m[1];
29509
+ if (seenService.has(name)) continue;
29510
+ seenService.add(name);
29511
+ service.push({ name, files: [filePath], imports: [] });
29512
+ }
29513
+ const seenRoute = /* @__PURE__ */ new Set();
29514
+ for (const m of source.matchAll(CPP_CROW_ROUTE_RE)) {
29515
+ const name = m[1];
29516
+ if (seenRoute.has(name)) continue;
29517
+ seenRoute.add(name);
29518
+ route.push({ name, files: [filePath], imports: [] });
29519
+ }
29520
+ for (const m of source.matchAll(CPP_DROGON_ROUTE_RE)) {
29521
+ const name = m[1] || m[2];
29522
+ if (!name) continue;
29523
+ if (seenRoute.has(name)) continue;
29524
+ seenRoute.add(name);
29525
+ route.push({ name, files: [filePath], imports: [] });
29526
+ }
29527
+ for (const m of source.matchAll(CPP_PISTACHE_ROUTE_RE)) {
29528
+ const name = m[1];
29529
+ if (seenRoute.has(name)) continue;
29530
+ seenRoute.add(name);
29531
+ route.push({ name, files: [filePath], imports: [] });
29532
+ }
29533
+ return { service, route, ormModel };
29534
+ }
29535
+ var SERVICE_SUFFIXES5, SERVICE_SUFFIX_GROUP5, CPP_SERVICE_RE, CPP_CROW_ROUTE_RE, CPP_DROGON_ROUTE_RE, CPP_PISTACHE_ROUTE_RE;
29536
+ var init_cpp = __esm({
29537
+ "src/engine/visitors/cpp.ts"() {
29538
+ "use strict";
29539
+ SERVICE_SUFFIXES5 = [
29540
+ "Service",
29541
+ "Manager",
29542
+ "Handler",
29543
+ "Repository",
29544
+ "Controller",
29545
+ "Helper",
29546
+ "Factory",
29547
+ "Provider",
29548
+ "Store",
29549
+ "API",
29550
+ "Client",
29551
+ "Adapter",
29552
+ "Resolver",
29553
+ "Mapper",
29554
+ "Transformer",
29555
+ "Serializer",
29556
+ "Validator",
29557
+ "Strategy",
29558
+ "Facade",
29559
+ "Decorator",
29560
+ "Observer",
29561
+ "Builder",
29562
+ "Command",
29563
+ "Processor",
29564
+ "Worker",
29565
+ "Job"
29566
+ ];
29567
+ SERVICE_SUFFIX_GROUP5 = `(?:${SERVICE_SUFFIXES5.join("|")})`;
29568
+ CPP_SERVICE_RE = new RegExp(
29569
+ `^(?:public\\s+|private\\s+|protected\\s+|virtual\\s+|abstract\\s+|static\\s+|inline\\s+|constexpr\\s+|explicit\\s+|final\\s+|extern\\s+)*(?:class|struct)\\s+(\\w+?)${SERVICE_SUFFIX_GROUP5}?\\b`,
29570
+ "gm"
29571
+ );
29572
+ CPP_CROW_ROUTE_RE = /CROW_ROUTE\s*\(\s*\w+\s*,\s*"(\/[^"]+)"/g;
29573
+ CPP_DROGON_ROUTE_RE = /\.registerHandler\(\s*"(\/[^"]+)"[\s,)]|\.\b(?:get|post|put|delete|patch)\(\s*"(\/[^"]+)"/g;
29574
+ CPP_PISTACHE_ROUTE_RE = /Routes::(?:get|post|put|delete|patch)\s*\(\s*[^,]+,\s*"(\/[^"]+)"/g;
29575
+ }
29576
+ });
29577
+
29578
+ // src/engine/visitors/java.ts
29579
+ var java_exports = {};
29580
+ __export(java_exports, {
29581
+ extractJavaPatterns: () => extractJavaPatterns
29582
+ });
29583
+ function extractJavaPatterns(filePath, source) {
29584
+ const service = [];
29585
+ const route = [];
29586
+ const ormModel = [];
29587
+ const seenService = /* @__PURE__ */ new Set();
29588
+ for (const m of source.matchAll(JAVA_SERVICE_RE)) {
29589
+ const name = m[1];
29590
+ if (seenService.has(name)) continue;
29591
+ seenService.add(name);
29592
+ service.push({ name, files: [filePath], imports: [] });
29593
+ }
29594
+ const seenRoute = /* @__PURE__ */ new Set();
29595
+ for (const m of source.matchAll(JAVA_SPRING_ROUTE_RE)) {
29596
+ const name = m[1];
29597
+ if (seenRoute.has(name)) continue;
29598
+ seenRoute.add(name);
29599
+ route.push({ name, files: [filePath], imports: [] });
29600
+ }
29601
+ for (const m of source.matchAll(JAVA_JAXRS_CLASS_PATH_RE)) {
29602
+ const name = m[1];
29603
+ if (name === "" || name === "/") continue;
29604
+ if (seenRoute.has(name)) continue;
29605
+ seenRoute.add(name);
29606
+ route.push({ name, files: [filePath], imports: [] });
29607
+ }
29608
+ const seenOrm = /* @__PURE__ */ new Set();
29609
+ for (const m of source.matchAll(JAVA_ORM_ANNOTATION_RE)) {
29610
+ const name = m[1];
29611
+ if (seenOrm.has(name)) continue;
29612
+ seenOrm.add(name);
29613
+ ormModel.push({ name, files: [filePath], imports: [] });
29614
+ }
29615
+ for (const m of source.matchAll(JAVA_ORM_INHERIT_RE)) {
29616
+ const name = m[1];
29617
+ if (seenOrm.has(name)) continue;
29618
+ seenOrm.add(name);
29619
+ ormModel.push({ name, files: [filePath], imports: [] });
29620
+ }
29621
+ return { service, route, ormModel };
29622
+ }
29623
+ var SERVICE_SUFFIXES6, SERVICE_SUFFIX_GROUP6, JAVA_SERVICE_RE, JAVA_SPRING_ROUTE_RE, JAVA_JAXRS_CLASS_PATH_RE, JAVA_ORM_ANNOTATION_RE, JAVA_ORM_INHERIT_RE;
29624
+ var init_java = __esm({
29625
+ "src/engine/visitors/java.ts"() {
29626
+ "use strict";
29627
+ SERVICE_SUFFIXES6 = [
29238
29628
  "Service",
29239
29629
  "Manager",
29240
29630
  "Handler",
@@ -30945,6 +31335,246 @@ var init_broken_link = __esm({
30945
31335
  }
30946
31336
  });
30947
31337
 
31338
+ // src/rules/dup/identical-block.ts
31339
+ import * as crypto from "crypto";
31340
+ function normalizeAndHash(lines) {
31341
+ const normalized = lines.map(
31342
+ (line) => line.replace(/\/\/.*$/, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").trim()
31343
+ ).filter((line) => line.length > 0).join("\n");
31344
+ if (normalized.length < MIN_NORMALIZED_LENGTH) return void 0;
31345
+ return crypto.createHash("sha1").update(normalized).digest("hex").slice(0, HASH_PREFIX_LENGTH);
31346
+ }
31347
+ var WINDOW_SIZE, MIN_NORMALIZED_LENGTH, HASH_PREFIX_LENGTH, DEDUP_CACHE, dupIdenticalBlockRule;
31348
+ var init_identical_block = __esm({
31349
+ "src/rules/dup/identical-block.ts"() {
31350
+ "use strict";
31351
+ init_rule();
31352
+ WINDOW_SIZE = 10;
31353
+ MIN_NORMALIZED_LENGTH = 40;
31354
+ HASH_PREFIX_LENGTH = 16;
31355
+ DEDUP_CACHE = /* @__PURE__ */ new Map();
31356
+ dupIdenticalBlockRule = createRule({
31357
+ id: "dup/identical-block",
31358
+ category: "logic",
31359
+ severity: "medium",
31360
+ aiSpecific: false,
31361
+ description: "Block of >=10 lines is identical across >=2 files (Type-1 clone detector)",
31362
+ create(_context) {
31363
+ return {};
31364
+ },
31365
+ analyze(_context, facts) {
31366
+ const issues = [];
31367
+ const source = facts.v2?._source;
31368
+ if (!source) return issues;
31369
+ const filePath = facts.filePath;
31370
+ const lines = source.split("\n");
31371
+ for (let i = 0; i <= lines.length - WINDOW_SIZE; i++) {
31372
+ const window = lines.slice(i, i + WINDOW_SIZE);
31373
+ const hash = normalizeAndHash(window);
31374
+ if (!hash) continue;
31375
+ const existing = DEDUP_CACHE.get(hash) ?? [];
31376
+ const matches = existing.filter((m) => m.file !== filePath);
31377
+ for (const match of matches) {
31378
+ issues.push({
31379
+ ruleId: "dup/identical-block",
31380
+ category: "logic",
31381
+ severity: "medium",
31382
+ aiSpecific: false,
31383
+ message: `Identical ${WINDOW_SIZE}-line block at line ${i + 1} also appears in ${match.file}:${match.line + 1}`,
31384
+ line: i + 1,
31385
+ column: 0,
31386
+ advice: "Refactor to a shared helper. This is a Type-1 clone (byte-for-byte identical after normalization). Common in AI-generated code that copy-pastes from training data.",
31387
+ extras: {
31388
+ duplicateOf: {
31389
+ file: match.file,
31390
+ line: match.line + 1,
31391
+ hash
31392
+ }
31393
+ }
31394
+ });
31395
+ }
31396
+ existing.push({ file: filePath, line: i });
31397
+ DEDUP_CACHE.set(hash, existing);
31398
+ }
31399
+ return issues;
31400
+ }
31401
+ });
31402
+ }
31403
+ });
31404
+
31405
+ // src/rules/go/error-wrap-without-context.ts
31406
+ var ERR_WRAP_REGEX, GENERIC_PREFIX_REGEX, goErrorWrapWithoutContextRule;
31407
+ var init_error_wrap_without_context = __esm({
31408
+ "src/rules/go/error-wrap-without-context.ts"() {
31409
+ "use strict";
31410
+ init_rule();
31411
+ ERR_WRAP_REGEX = /fmt\.Errorf\(\s*"((?:[^"\\]|\\.)*)"\s*,\s*([^)]+)\)/g;
31412
+ GENERIC_PREFIX_REGEX = /^\s*(?:error|err|failed|wrap(?:ping)?|invalid)\s*(?::\s*)?%w\b/i;
31413
+ goErrorWrapWithoutContextRule = createRule({
31414
+ id: "go/error-wrap-without-context",
31415
+ category: "typo",
31416
+ severity: "low",
31417
+ aiSpecific: true,
31418
+ description: 'fmt.Errorf wrap without operation context \u2014 AI defaults to generic "error: %w"',
31419
+ create(_context) {
31420
+ return {};
31421
+ },
31422
+ analyze(_context, facts) {
31423
+ const issues = [];
31424
+ const source = facts.v2?._source;
31425
+ if (!source) return issues;
31426
+ let match;
31427
+ ERR_WRAP_REGEX.lastIndex = 0;
31428
+ while ((match = ERR_WRAP_REGEX.exec(source)) !== null) {
31429
+ const formatString = match[1];
31430
+ if (!formatString.includes("%w")) continue;
31431
+ if (formatString.length >= 30) continue;
31432
+ if (!GENERIC_PREFIX_REGEX.test(formatString)) continue;
31433
+ const line = source.slice(0, match.index).split("\n").length;
31434
+ issues.push({
31435
+ ruleId: "go/error-wrap-without-context",
31436
+ category: "typo",
31437
+ severity: "low",
31438
+ aiSpecific: true,
31439
+ message: `fmt.Errorf wrap with generic message "${formatString}" \u2014 include the failing operation`,
31440
+ line,
31441
+ column: match[0].indexOf("fmt") + 1,
31442
+ advice: 'Real Go errors include the failing operation: `fmt.Errorf("opening config: %w", err)`. Generic messages ("error: %w", "failed: %w") tell the reader nothing about what failed. Reference: go/error-wrap-without-context v0.19. See: https://github.com/golang/go/wiki/CodeReviewComments#error-strings'
31443
+ });
31444
+ }
31445
+ return issues;
31446
+ }
31447
+ });
31448
+ }
31449
+ });
31450
+
31451
+ // src/rules/go/nil-slice-vs-empty.ts
31452
+ var NIL_SLICE_DECL_REGEX, EMPTY_SLICE_ASSIGN_REGEX, goNilSliceVsEmptyRule;
31453
+ var init_nil_slice_vs_empty = __esm({
31454
+ "src/rules/go/nil-slice-vs-empty.ts"() {
31455
+ "use strict";
31456
+ init_rule();
31457
+ NIL_SLICE_DECL_REGEX = /^[\t ]*var\s+([A-Za-z_][A-Za-z0-9_]*)\s+\[\][\w.*]+\b/gm;
31458
+ EMPTY_SLICE_ASSIGN_REGEX = /^[\t ]*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?:\[\][\w.*]*\{\}|make\(\[\][\w.*]*)/gm;
31459
+ goNilSliceVsEmptyRule = createRule({
31460
+ id: "go/nil-slice-vs-empty",
31461
+ category: "typo",
31462
+ severity: "low",
31463
+ aiSpecific: true,
31464
+ description: "Variable declared `var x []int` but later assigned `x = []int{}` or `make([]int, n)` \u2014 pick one form",
31465
+ create(_context) {
31466
+ return {};
31467
+ },
31468
+ analyze(_context, facts) {
31469
+ const issues = [];
31470
+ const source = facts.v2?._source;
31471
+ if (!source) return issues;
31472
+ const nilDecls = /* @__PURE__ */ new Map();
31473
+ let m;
31474
+ NIL_SLICE_DECL_REGEX.lastIndex = 0;
31475
+ while ((m = NIL_SLICE_DECL_REGEX.exec(source)) !== null) {
31476
+ nilDecls.set(m[1], source.slice(0, m.index).split("\n").length);
31477
+ }
31478
+ if (nilDecls.size === 0) return issues;
31479
+ EMPTY_SLICE_ASSIGN_REGEX.lastIndex = 0;
31480
+ while ((m = EMPTY_SLICE_ASSIGN_REGEX.exec(source)) !== null) {
31481
+ const name = m[1];
31482
+ if (!nilDecls.has(name)) continue;
31483
+ const declLine = nilDecls.get(name);
31484
+ const assignLine = source.slice(0, m.index).split("\n").length;
31485
+ if (assignLine <= declLine) continue;
31486
+ issues.push({
31487
+ ruleId: "go/nil-slice-vs-empty",
31488
+ category: "typo",
31489
+ severity: "low",
31490
+ aiSpecific: true,
31491
+ message: `Variable '${name}' declared as nil slice (line ${declLine}) but assigned an empty slice (line ${assignLine}) \u2014 pick one form`,
31492
+ line: declLine,
31493
+ column: 1,
31494
+ advice: "Either declare as `var " + name + " = []int{}` or assign with `make([]int, 0)`. The nil/empty inconsistency is an AI signal \u2014 real code picks one form and sticks with it. Reference: go/nil-slice-vs-empty v0.19."
31495
+ });
31496
+ }
31497
+ return issues;
31498
+ }
31499
+ });
31500
+ }
31501
+ });
31502
+
31503
+ // src/rules/go/struct-tag-inconsistency.ts
31504
+ var JSON_TAG_REGEX, goStructTagInconsistencyRule;
31505
+ var init_struct_tag_inconsistency = __esm({
31506
+ "src/rules/go/struct-tag-inconsistency.ts"() {
31507
+ "use strict";
31508
+ init_rule();
31509
+ JSON_TAG_REGEX = /`json:"([^",]+)(?:,([^"]+))?"`/g;
31510
+ goStructTagInconsistencyRule = createRule({
31511
+ id: "go/struct-tag-inconsistency",
31512
+ category: "typo",
31513
+ severity: "low",
31514
+ aiSpecific: true,
31515
+ description: 'Struct fields mix json:"foo" and json:"foo,omitempty" \u2014 pick one convention per struct',
31516
+ create(_context) {
31517
+ return {};
31518
+ },
31519
+ analyze(_context, facts) {
31520
+ const issues = [];
31521
+ const source = facts.v2?._source;
31522
+ if (!source) return issues;
31523
+ const structRegex = /type\s+[A-Z][A-Za-z0-9_]*\s+struct\s*\{/g;
31524
+ let structMatch;
31525
+ while ((structMatch = structRegex.exec(source)) !== null) {
31526
+ const startIdx = structMatch.index;
31527
+ const openBrace = source.indexOf("{", startIdx);
31528
+ if (openBrace < 0) continue;
31529
+ let depth = 1;
31530
+ let i = openBrace + 1;
31531
+ while (i < source.length && depth > 0) {
31532
+ const ch = source[i];
31533
+ if (ch === "{") depth++;
31534
+ else if (ch === "}") depth--;
31535
+ i++;
31536
+ }
31537
+ const structBody = source.slice(openBrace, i);
31538
+ const structLine = source.slice(0, startIdx).split("\n").length;
31539
+ const styleCount = {};
31540
+ const tagMatches = [];
31541
+ let m;
31542
+ JSON_TAG_REGEX.lastIndex = 0;
31543
+ while ((m = JSON_TAG_REGEX.exec(structBody)) !== null) {
31544
+ const tag = m[1];
31545
+ const options = m[2] ?? "";
31546
+ const style = options ? "with-options" : "no-options";
31547
+ styleCount[style] = (styleCount[style] ?? 0) + 1;
31548
+ tagMatches.push({ tag, style, idx: openBrace + m.index });
31549
+ }
31550
+ const styles = Object.keys(styleCount);
31551
+ if (styles.length < 2 || tagMatches.length < 2) continue;
31552
+ const dominant = styles.reduce(
31553
+ (a, b) => (styleCount[a] ?? 0) >= (styleCount[b] ?? 0) ? a : b
31554
+ );
31555
+ const minority = tagMatches.filter((t) => t.style !== dominant);
31556
+ if (minority.length === 0) continue;
31557
+ for (const m2 of minority) {
31558
+ const line = source.slice(0, m2.idx).split("\n").length;
31559
+ issues.push({
31560
+ ruleId: "go/struct-tag-inconsistency",
31561
+ category: "typo",
31562
+ severity: "low",
31563
+ aiSpecific: true,
31564
+ message: `Struct mixes json tag styles \u2014 this field uses "json:"${m2.tag}${m2.style === "with-options" ? ",..." : ""}"" but the dominant style is ${dominant === "with-options" ? "with options (e.g. omitempty)" : "no options"}`,
31565
+ line,
31566
+ column: 1,
31567
+ advice: 'Pick one tag style per struct. If most fields are `json:"foo"`, this field should be too. Real Go code maintains consistency within a struct (or within a package). Reference: go/struct-tag-inconsistency v0.19.'
31568
+ });
31569
+ }
31570
+ if (issues.length > 0) break;
31571
+ }
31572
+ return issues;
31573
+ }
31574
+ });
31575
+ }
31576
+ });
31577
+
30948
31578
  // src/rules/layout/gap-monopoly.ts
30949
31579
  var GAP_RE, gapMonopolyRule;
30950
31580
  var init_gap_monopoly = __esm({
@@ -35835,11 +36465,11 @@ __export(dist_exports2, {
35835
36465
  tokenizeIdentifiers: () => tokenizeIdentifiers
35836
36466
  });
35837
36467
  import { readFile, writeFile, mkdir, access } from "fs/promises";
35838
- import { createHash } from "crypto";
36468
+ import { createHash as createHash2 } from "crypto";
35839
36469
  import { join as join10, dirname as dirname6 } from "path";
35840
36470
  import { parseSync } from "@swc/core";
35841
36471
  import { join as join22 } from "path";
35842
- import { createHash as createHash2 } from "crypto";
36472
+ import { createHash as createHash22 } from "crypto";
35843
36473
  import { createHash as createHash3 } from "crypto";
35844
36474
  import { readFile as readFile2 } from "fs/promises";
35845
36475
  import { relative as relative3 } from "path";
@@ -36051,7 +36681,7 @@ function parseBlankModule(source) {
36051
36681
  tsx: false,
36052
36682
  target: "es2022"
36053
36683
  });
36054
- return { ast, source: replaced };
36684
+ return { ast, source };
36055
36685
  }
36056
36686
  function parseScriptContent(content, isTypeScript) {
36057
36687
  if (isTypeScript) {
@@ -36093,7 +36723,7 @@ function legacyCacheRoot() {
36093
36723
  return join10(process.cwd(), ".slopbrick", "cache", "ast");
36094
36724
  }
36095
36725
  function hashContent(content) {
36096
- return createHash("md5").update(content, "utf-8").digest("hex");
36726
+ return createHash2("md5").update(content, "utf-8").digest("hex");
36097
36727
  }
36098
36728
  function cachePathWithRoot(content, root) {
36099
36729
  return join10(root, `${hashContent(content)}.json`);
@@ -36137,8 +36767,16 @@ function parseSource(source, filePath) {
36137
36767
  // but no SWC support. Return a blank-padded empty module so
36138
36768
  // regex-only rules can still fire (markdown-leakage, comment-
36139
36769
  // ratio, etc.) without burning the parseError path.
36770
+ // v0.18.9: `.rs` is in the same bucket. The visitor layer
36771
+ // (`engine/visitors/rust.ts`) carries the tree-sitter-backed
36772
+ // parse + extractFacts attaches the result to `facts.v2.rustFile`.
36773
+ // The rule layer (`rules/rust/*.ts`) reads that field. parseBlankModule
36774
+ // lets the worker continue past the parser — without this, parseWithSwc
36775
+ // would throw on every .rs file, the worker would count it as a
36776
+ // parseError, and the v2-build's buildRustFileRecord would never run.
36140
36777
  case "py":
36141
36778
  case "go":
36779
+ case "rs":
36142
36780
  return parseBlankModule(source);
36143
36781
  default:
36144
36782
  return parseWithSwc(source, filePath);
@@ -36283,7 +36921,7 @@ function fingerprintFor(component) {
36283
36921
  const hooks = component.hookCalls.map((h) => h.name).sort();
36284
36922
  const props = component.props.map((p) => p.name).sort();
36285
36923
  const payload = `${component.name}|${hooks.join(",")}|${props.join(",")}`;
36286
- return createHash2("sha256").update(payload).digest("hex").slice(0, 16);
36924
+ return createHash22("sha256").update(payload).digest("hex").slice(0, 16);
36287
36925
  }
36288
36926
  function buildComponentFingerprints(results) {
36289
36927
  const byName = /* @__PURE__ */ new Map();
@@ -38225,91 +38863,6 @@ var init_key_prop_missing = __esm({
38225
38863
  }
38226
38864
  });
38227
38865
 
38228
- // src/rules/logic/ks-distribution-shift.ts
38229
- function extractFileFeatures(source) {
38230
- const lines = source.split("\n");
38231
- const lineLengths = lines.map((l) => l.length);
38232
- const identifierLengths = [];
38233
- const idRe = /[A-Za-z_$][A-Za-z0-9_$]*/g;
38234
- let m;
38235
- while ((m = idRe.exec(source)) !== null) {
38236
- identifierLengths.push(m[0].length);
38237
- }
38238
- const commentDensity = lines.map((l) => {
38239
- const trimmed = l.trim();
38240
- if (trimmed.length === 0) return 0;
38241
- const commentChars = (trimmed.match(/^\/\/.*$/)?.[0]?.length ?? 0) + (trimmed.match(/^\s*\/\*.*?\*\/\s*$/)?.at(0)?.length ?? 0);
38242
- return commentChars / trimmed.length;
38243
- });
38244
- return { lineLengths, identifierLengths, commentDensity };
38245
- }
38246
- var MIN_SAMPLES_PER_FEATURE, ksDistributionShiftRule;
38247
- var init_ks_distribution_shift = __esm({
38248
- "src/rules/logic/ks-distribution-shift.ts"() {
38249
- "use strict";
38250
- init_rule();
38251
- init_dist2();
38252
- init_corpus_baselines2();
38253
- MIN_SAMPLES_PER_FEATURE = 20;
38254
- ksDistributionShiftRule = createRule({
38255
- id: "logic/ks-distribution-shift",
38256
- category: "logic",
38257
- severity: "medium",
38258
- aiSpecific: false,
38259
- description: "Multi-feature Kolmogorov\u2013Smirnov distribution-shift vs corpus baseline (Bonferroni-corrected). Peer-reviewed ML distribution-shift detector (arXiv:2510.15996, Oct 2025).",
38260
- create(context) {
38261
- return context;
38262
- },
38263
- analyze(_context, facts) {
38264
- const issues = [];
38265
- if (!facts.v2) return issues;
38266
- const source = facts.v2._source ?? "";
38267
- if (source.length < 200) return issues;
38268
- const features = extractFileFeatures(source);
38269
- const samples = /* @__PURE__ */ new Map([
38270
- ["lineLengths", features.lineLengths],
38271
- ["identifierLengths", features.identifierLengths],
38272
- ["commentDensity", features.commentDensity]
38273
- ]);
38274
- const baselines = getCorpusBaselines();
38275
- const baselinesMap = /* @__PURE__ */ new Map();
38276
- if (baselines) {
38277
- baselinesMap.set("lineLengths", baselines.features.lineLengths.sample);
38278
- baselinesMap.set("identifierLengths", baselines.features.identifierLengths.sample);
38279
- baselinesMap.set("commentDensity", baselines.features.commentDensity.sample);
38280
- } else {
38281
- baselinesMap.set("lineLengths", [20, 25, 30, 32, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]);
38282
- baselinesMap.set("identifierLengths", [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 28]);
38283
- baselinesMap.set("commentDensity", [0, 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5]);
38284
- }
38285
- for (const [name, vals] of samples) {
38286
- if (vals.length < MIN_SAMPLES_PER_FEATURE) samples.delete(name);
38287
- }
38288
- if (samples.size === 0) return issues;
38289
- const result = multiFeatureKsTest(samples, baselinesMap, 0.05);
38290
- if (!result.anySignificant) return issues;
38291
- const shifted = result.significantFeatures.join(", ");
38292
- const details = result.significantFeatures.map((name) => {
38293
- const r = result.perFeature.get(name);
38294
- if (!r) return name;
38295
- return `${name} (D=${r.statistic.toFixed(3)}, p=${r.pValue.toExponential(2)})`;
38296
- }).join("; ");
38297
- issues.push({
38298
- ruleId: "logic/ks-distribution-shift",
38299
- category: "logic",
38300
- severity: "medium",
38301
- aiSpecific: true,
38302
- message: `Distribution shift detected on ${result.significantFeatures.length} of ${result.perFeature.size} features (Bonferroni \u03B1=${result.bonferroniAlpha.toExponential(2)}). Features: ${shifted}. Detail: ${details}.`,
38303
- line: 1,
38304
- column: 1,
38305
- advice: "Inspect the shifted features. KS detects both AI anomalies and production-rot anomalies (it is symmetric); combine with Heaps/Zipf for AI-specific signal."
38306
- });
38307
- return issues;
38308
- }
38309
- });
38310
- }
38311
- });
38312
-
38313
38866
  // src/rules/logic/math-any-density.ts
38314
38867
  var ANY_PER_100_LINES, MIN_ABSOLUTE, COLON, ANY_KEYWORD, ANY_TYPE_RE, MSG_FRAGMENT, RULE_DESCRIPTION, RULE_MESSAGE, RULE_ADVICE, mathAnyDensityRule;
38315
38868
  var init_math_any_density = __esm({
@@ -38379,13 +38932,13 @@ var init_math_any_density = __esm({
38379
38932
  });
38380
38933
 
38381
38934
  // src/rules/logic/math-console-log-storm.ts
38382
- var WINDOW_SIZE, STORM_THRESHOLD, CONSOLE_LOG_RE, mathConsoleLogStormRule;
38935
+ var WINDOW_SIZE2, STORM_THRESHOLD, CONSOLE_LOG_RE, mathConsoleLogStormRule;
38383
38936
  var init_math_console_log_storm = __esm({
38384
38937
  "src/rules/logic/math-console-log-storm.ts"() {
38385
38938
  "use strict";
38386
38939
  init_rule();
38387
38940
  init_utils();
38388
- WINDOW_SIZE = 30;
38941
+ WINDOW_SIZE2 = 30;
38389
38942
  STORM_THRESHOLD = 5;
38390
38943
  CONSOLE_LOG_RE = /\bconsole\.log\s*\(/g;
38391
38944
  mathConsoleLogStormRule = createRule({
@@ -38416,7 +38969,7 @@ var init_math_console_log_storm = __esm({
38416
38969
  let maxEndLine = 0;
38417
38970
  let i = 0;
38418
38971
  for (let j = 0; j < lines.length; j++) {
38419
- while (lines[j] - lines[i] > WINDOW_SIZE) i++;
38972
+ while (lines[j] - lines[i] > WINDOW_SIZE2) i++;
38420
38973
  const count = j - i + 1;
38421
38974
  if (count > maxCount) {
38422
38975
  maxCount = count;
@@ -38430,7 +38983,7 @@ var init_math_console_log_storm = __esm({
38430
38983
  category: "logic",
38431
38984
  severity: "high",
38432
38985
  aiSpecific: true,
38433
- message: `${maxCount} console.log calls clustered in a ${WINDOW_SIZE}-line window ending at line ${maxEndLine}. AI debug-sprays logs in a single function; humans use one strategic log.`,
38986
+ message: `${maxCount} console.log calls clustered in a ${WINDOW_SIZE2}-line window ending at line ${maxEndLine}. AI debug-sprays logs in a single function; humans use one strategic log.`,
38434
38987
  line: firstIdx >= 0 ? lines[firstIdx] : 1,
38435
38988
  column: firstIdx >= 0 ? columns[firstIdx] : 1,
38436
38989
  advice: "Replace debug logs with a proper debugger or logger.debug() \u2014 remove all console.log before shipping."
@@ -39316,6 +39869,354 @@ var init_ux_pattern_fragmentation = __esm({
39316
39869
  }
39317
39870
  });
39318
39871
 
39872
+ // src/rules/rust/stringly-typed.ts
39873
+ function collectEnumCandidates(source) {
39874
+ const out = [];
39875
+ const enumRe = /^(?:pub(?:\([^)]+\))?\s+)?enum\s+(\w+)\s*\{([^}]*)\}/gm;
39876
+ for (const m of source.matchAll(enumRe)) {
39877
+ const body = m[2] ?? "";
39878
+ const variants = body.split(/,(?![^()]*\))/).map((s) => s.trim()).filter((s) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s.split(/\s+/)[0] ?? ""));
39879
+ if (variants.length > MAX_VARIANT_COUNT) continue;
39880
+ if (variants.length < 2) continue;
39881
+ out.push({ name: m[1], variantCount: variants.length, line: lineOfMatch(source, m.index ?? 0) });
39882
+ }
39883
+ return out;
39884
+ }
39885
+ function lineOfMatch(source, index) {
39886
+ let line = 1;
39887
+ for (let i = 0; i < index && i < source.length; i++) {
39888
+ if (source[i] === "\n") line++;
39889
+ }
39890
+ return line;
39891
+ }
39892
+ function buildLineOffsets(source) {
39893
+ const out = [0];
39894
+ for (let i = 0; i < source.length; i++) {
39895
+ if (source[i] === "\n") out.push(i + 1);
39896
+ }
39897
+ return out;
39898
+ }
39899
+ function extractParameterText(source, lineOffsets, fn) {
39900
+ const start = lineOffsets[Math.max(0, fn.line - 1)] ?? 0;
39901
+ const head = source.slice(start, start + 400);
39902
+ const openIdx = head.indexOf("(");
39903
+ if (openIdx < 0) return "";
39904
+ let closeIdx = -1;
39905
+ let depth = 0;
39906
+ for (let i = openIdx; i < head.length; i++) {
39907
+ if (head[i] === "(") depth++;
39908
+ else if (head[i] === ")") {
39909
+ depth--;
39910
+ if (depth === 0) {
39911
+ closeIdx = i;
39912
+ break;
39913
+ }
39914
+ }
39915
+ }
39916
+ if (closeIdx < 0) return "";
39917
+ return head.slice(openIdx + 1, closeIdx);
39918
+ }
39919
+ function scanForStringlyParams(paramText) {
39920
+ const out = [];
39921
+ for (const m of paramText.matchAll(
39922
+ /\b([a-z_][a-zA-Z0-9_]*)\s*:\s*(&\s*(?:mut\s+)?(?:str|String)\b)/g
39923
+ )) {
39924
+ const name = m[1];
39925
+ const type = m[2];
39926
+ if (!SUSPECT_PARAM_NAMES.has(name)) continue;
39927
+ out.push({ name, type });
39928
+ }
39929
+ return out;
39930
+ }
39931
+ var SUSPECT_PARAM_NAMES, MAX_VARIANT_COUNT, rustStringlyTypedRule;
39932
+ var init_stringly_typed = __esm({
39933
+ "src/rules/rust/stringly-typed.ts"() {
39934
+ "use strict";
39935
+ init_rule();
39936
+ SUSPECT_PARAM_NAMES = /* @__PURE__ */ new Set([
39937
+ "kind",
39938
+ "type",
39939
+ "mode",
39940
+ "event",
39941
+ "status",
39942
+ "category",
39943
+ "action",
39944
+ "state",
39945
+ "level",
39946
+ "role",
39947
+ "tier",
39948
+ "phase",
39949
+ "tag",
39950
+ "format",
39951
+ "shape",
39952
+ "direction",
39953
+ "side",
39954
+ "method"
39955
+ ]);
39956
+ MAX_VARIANT_COUNT = 32;
39957
+ rustStringlyTypedRule = createRule({
39958
+ id: "rust/stringly-typed",
39959
+ category: "logic",
39960
+ severity: "medium",
39961
+ aiSpecific: true,
39962
+ description: "String / &str parameter where a typed enum exists in the same file",
39963
+ create(_context) {
39964
+ return {};
39965
+ },
39966
+ analyze(_context, facts) {
39967
+ const issues = [];
39968
+ if (!facts.v2?.rustFile) return issues;
39969
+ const source = facts.v2._source ?? "";
39970
+ if (!source) return issues;
39971
+ const lineOffsets = buildLineOffsets(source);
39972
+ const enumCandidates = collectEnumCandidates(source);
39973
+ if (enumCandidates.length === 0) return issues;
39974
+ for (const fn of facts.v2.rustFile.functions) {
39975
+ const paramText = extractParameterText(source, lineOffsets, fn);
39976
+ if (!paramText) continue;
39977
+ const matches = scanForStringlyParams(paramText);
39978
+ if (matches.length === 0) continue;
39979
+ issues.push({
39980
+ ruleId: "rust/stringly-typed",
39981
+ category: "logic",
39982
+ severity: "medium",
39983
+ aiSpecific: true,
39984
+ message: matches.length === 1 ? `Parameter '${matches[0].name}' typed as '${matches[0].type}', but enum ${enumCandidates[0].name} exists in the file` : `Function has ${matches.length} stringly-typed parameters; enum ${enumCandidates[0].name} exists in the file`,
39985
+ line: fn.line,
39986
+ column: fn.column,
39987
+ advice: `Replace the String/&str parameter with the typed enum. Stringly-typed APIs lose type information at the boundary; a typo ('Click' vs 'click') only fails at runtime. AI agents introduce these during exploratory scaffolding, then never replace them with the existing enum.`
39988
+ });
39989
+ }
39990
+ return issues;
39991
+ }
39992
+ });
39993
+ }
39994
+ });
39995
+
39996
+ // src/rules/rust/todo-macro.ts
39997
+ function collectMacroIssues(node, testScopes, issues) {
39998
+ if (node.type === "macro_invocation") {
39999
+ const text = node.text;
40000
+ const m = text.match(/^([A-Za-z_][A-Za-z0-9_]*)/);
40001
+ const macroName = m?.[1] ?? "";
40002
+ if (TODO_MACROS.has(macroName)) {
40003
+ if (!isInsideMacroDefinition(node) && !isInsideTestFunction(node, testScopes)) {
40004
+ issues.push({
40005
+ ruleId: "rust/todo-macro",
40006
+ category: "logic",
40007
+ severity: "medium",
40008
+ aiSpecific: true,
40009
+ message: `'${macroName}!()' in production code \u2014 both expand to panic!()`,
40010
+ line: node.startPosition.row + 1,
40011
+ column: node.startPosition.column,
40012
+ advice: `Implement the function body or remove the stub. '${macroName}!()' is fine in test scaffolding (#[cfg(test)]); here it is a panic risk. AI agents commonly leave these behind after iterative refactors when the placeholder branch is never filled in.`
40013
+ });
40014
+ }
40015
+ }
40016
+ }
40017
+ for (let i = 0; i < node.namedChildCount; i++) {
40018
+ const child = node.namedChild(i);
40019
+ if (child) collectMacroIssues(child, testScopes, issues);
40020
+ }
40021
+ }
40022
+ function isInsideMacroDefinition(node) {
40023
+ for (let p = node.parent; p; p = p.parent) {
40024
+ if (p.type === "macro_definition") return true;
40025
+ if (p.type === "source_file") return false;
40026
+ }
40027
+ return false;
40028
+ }
40029
+ function isInsideTestFunction(node, testScopes) {
40030
+ for (let p = node.parent; p; p = p.parent) {
40031
+ if (p.type === "function_item") {
40032
+ const nameField = p.childForFieldName("name");
40033
+ const name = nameField?.text;
40034
+ if (name && testScopes.has(name)) return true;
40035
+ if (p.text.startsWith("#[test]") || p.text.startsWith("#[cfg(test)]")) return true;
40036
+ return false;
40037
+ }
40038
+ if (p.type === "source_file") return false;
40039
+ }
40040
+ return false;
40041
+ }
40042
+ var TODO_MACROS, rustTodoMacroRule;
40043
+ var init_todo_macro = __esm({
40044
+ "src/rules/rust/todo-macro.ts"() {
40045
+ "use strict";
40046
+ init_rule();
40047
+ init_parser_rust();
40048
+ TODO_MACROS = /* @__PURE__ */ new Set(["todo", "unimplemented", "todo_unimplemented"]);
40049
+ rustTodoMacroRule = createRule({
40050
+ id: "rust/todo-macro",
40051
+ category: "logic",
40052
+ severity: "medium",
40053
+ aiSpecific: true,
40054
+ description: "todo!() / unimplemented!() macro invocation in production code",
40055
+ create(_context) {
40056
+ return {};
40057
+ },
40058
+ analyze(_context, facts) {
40059
+ const issues = [];
40060
+ if (!facts.v2?.rustFile) return issues;
40061
+ const source = facts.v2._source ?? "";
40062
+ if (!source) return issues;
40063
+ const tree = parseRust(source);
40064
+ if (!tree) return issues;
40065
+ const testScopes = /* @__PURE__ */ new Set();
40066
+ for (const fn of facts.v2.rustFile.functions) {
40067
+ if (fn.inTestConfig && fn.name) testScopes.add(fn.name);
40068
+ }
40069
+ collectMacroIssues(tree.rootNode, testScopes, issues);
40070
+ return issues;
40071
+ }
40072
+ });
40073
+ }
40074
+ });
40075
+
40076
+ // src/rules/rust/unused-pub-fn.ts
40077
+ function collectReferencedNames(source) {
40078
+ const out = /* @__PURE__ */ new Set();
40079
+ for (const m of source.matchAll(/\b([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
40080
+ out.add(m[1]);
40081
+ }
40082
+ return out;
40083
+ }
40084
+ var API_CONVENTION_NAMES, rustUnusedPubFnRule;
40085
+ var init_unused_pub_fn = __esm({
40086
+ "src/rules/rust/unused-pub-fn.ts"() {
40087
+ "use strict";
40088
+ init_rule();
40089
+ API_CONVENTION_NAMES = /* @__PURE__ */ new Set([
40090
+ "new",
40091
+ "default",
40092
+ "from",
40093
+ "from_str",
40094
+ "from_iter",
40095
+ "try_from",
40096
+ "into",
40097
+ "into_iter",
40098
+ "try_into",
40099
+ "as_ref",
40100
+ "as_mut",
40101
+ "clone",
40102
+ "fmt",
40103
+ "eq",
40104
+ "hash",
40105
+ "partial_cmp",
40106
+ "cmp",
40107
+ "ord",
40108
+ "partial_eq"
40109
+ ]);
40110
+ rustUnusedPubFnRule = createRule({
40111
+ id: "rust/unused-pub-fn",
40112
+ category: "logic",
40113
+ severity: "low",
40114
+ aiSpecific: true,
40115
+ description: "Public function in a Rust file that has no in-file references",
40116
+ create(_context) {
40117
+ return {};
40118
+ },
40119
+ analyze(_context, facts) {
40120
+ const issues = [];
40121
+ if (!facts.v2?.rustFile) return issues;
40122
+ const rust = facts.v2.rustFile;
40123
+ const referenced = collectReferencedNames(facts.v2._source ?? "");
40124
+ for (const fn of rust.functions) {
40125
+ if (!fn.isPublic) continue;
40126
+ if (API_CONVENTION_NAMES.has(fn.name)) continue;
40127
+ if (fn.inTestConfig) continue;
40128
+ if (referenced.has(fn.name)) continue;
40129
+ issues.push({
40130
+ ruleId: "rust/unused-pub-fn",
40131
+ category: "logic",
40132
+ severity: "low",
40133
+ aiSpecific: true,
40134
+ message: `Public function '${fn.name}' is not referenced anywhere in the file`,
40135
+ line: fn.line,
40136
+ column: fn.column,
40137
+ advice: `Remove the function or call it somewhere. Rust's compiler doesn't warn on pub fns missing consumers unless \`#![warn(dead_code)]\` is set at the crate root. AI agents that iterate on a file often leave these behind \u2014 the most common AI-rotation signature for Rust after unused-imports.`
40138
+ });
40139
+ }
40140
+ return issues;
40141
+ }
40142
+ });
40143
+ }
40144
+ });
40145
+
40146
+ // src/rules/rust/unwrap-in-production.ts
40147
+ function collectUnwrapIssues(node, testScopes, issues) {
40148
+ if (node.type === "call_expression") {
40149
+ const fn = node.childForFieldName("function");
40150
+ if (fn && fn.type === "field_expression") {
40151
+ const field = fn.childForFieldName("field");
40152
+ if (field && field.type === "field_identifier" && UNWRAP_METHODS.has(field.text)) {
40153
+ if (!isInsideTestFunction2(node, testScopes)) {
40154
+ issues.push({
40155
+ ruleId: "rust/unwrap-in-production",
40156
+ category: "logic",
40157
+ severity: "medium",
40158
+ aiSpecific: true,
40159
+ message: `'.${field.text}()' called in production code \u2014 panic risk on Err/None`,
40160
+ line: node.startPosition.row + 1,
40161
+ column: node.startPosition.column,
40162
+ advice: `Replace with '?' (early-return on Err), '.map_err(...)' for conversion, or an explicit 'match'. '.${field.text}()' is fine in tests \u2014 wrap with '#[cfg(test)]' or move into a '#[cfg(test)] mod tests' block to suppress.`
40163
+ });
40164
+ }
40165
+ }
40166
+ }
40167
+ }
40168
+ for (let i = 0; i < node.namedChildCount; i++) {
40169
+ const child = node.namedChild(i);
40170
+ if (child) collectUnwrapIssues(child, testScopes, issues);
40171
+ }
40172
+ }
40173
+ function isInsideTestFunction2(node, testScopes) {
40174
+ for (let p = node.parent; p; p = p.parent) {
40175
+ if (p.type === "function_item") {
40176
+ const nameField = p.childForFieldName("name");
40177
+ const name = nameField?.text;
40178
+ if (name && testScopes.has(name)) return true;
40179
+ if (p.text.startsWith("#[test]") || p.text.startsWith("#[cfg(test)]")) return true;
40180
+ return false;
40181
+ }
40182
+ if (p.type === "source_file") return false;
40183
+ }
40184
+ return false;
40185
+ }
40186
+ var UNWRAP_METHODS, rustUnwrapInProductionRule;
40187
+ var init_unwrap_in_production = __esm({
40188
+ "src/rules/rust/unwrap-in-production.ts"() {
40189
+ "use strict";
40190
+ init_rule();
40191
+ init_parser_rust();
40192
+ UNWRAP_METHODS = /* @__PURE__ */ new Set(["unwrap", "expect", "unwrap_or_else"]);
40193
+ rustUnwrapInProductionRule = createRule({
40194
+ id: "rust/unwrap-in-production",
40195
+ category: "logic",
40196
+ severity: "medium",
40197
+ aiSpecific: true,
40198
+ description: ".unwrap() / .expect() called outside of #[cfg(test)] / #[test] scope",
40199
+ create(_context) {
40200
+ return {};
40201
+ },
40202
+ analyze(_context, facts) {
40203
+ const issues = [];
40204
+ if (!facts.v2?.rustFile) return issues;
40205
+ const source = facts.v2._source ?? "";
40206
+ if (!source) return issues;
40207
+ const tree = parseRust(source);
40208
+ if (!tree) return issues;
40209
+ const testScopes = /* @__PURE__ */ new Set();
40210
+ for (const fn of facts.v2.rustFile.functions) {
40211
+ if (fn.inTestConfig && fn.name) testScopes.add(fn.name);
40212
+ }
40213
+ collectUnwrapIssues(tree.rootNode, testScopes, issues);
40214
+ return issues;
40215
+ }
40216
+ });
40217
+ }
40218
+ });
40219
+
39319
40220
  // src/rules/security/dangerous-cors.ts
39320
40221
  function scanForWildcardCors(source) {
39321
40222
  const hits = [];
@@ -40906,6 +41807,251 @@ var init_weak_assertion = __esm({
40906
41807
  }
40907
41808
  });
40908
41809
 
41810
+ // src/rules/ts/enum-vs-as-const.ts
41811
+ var ENUM_DECL_REGEX, tsEnumVsAsConstRule;
41812
+ var init_enum_vs_as_const = __esm({
41813
+ "src/rules/ts/enum-vs-as-const.ts"() {
41814
+ "use strict";
41815
+ init_rule();
41816
+ ENUM_DECL_REGEX = /^[ \t]*(?:export\s+)?(?:const\s+)?enum\s+[A-Z_][A-Za-z0-9_]*\s*\{/gm;
41817
+ tsEnumVsAsConstRule = createRule({
41818
+ id: "ts/enum-vs-as-const",
41819
+ category: "typo",
41820
+ severity: "low",
41821
+ aiSpecific: true,
41822
+ description: "Uses `enum` \u2014 modern TS prefers `as const` objects",
41823
+ create(_context) {
41824
+ return {};
41825
+ },
41826
+ analyze(_context, facts) {
41827
+ const issues = [];
41828
+ const source = facts.v2?._source;
41829
+ if (!source) return issues;
41830
+ let match;
41831
+ ENUM_DECL_REGEX.lastIndex = 0;
41832
+ while ((match = ENUM_DECL_REGEX.exec(source)) !== null) {
41833
+ const line = source.slice(0, match.index).split("\n").length;
41834
+ issues.push({
41835
+ ruleId: "ts/enum-vs-as-const",
41836
+ category: "typo",
41837
+ severity: "low",
41838
+ aiSpecific: true,
41839
+ message: `'enum' is an AI / older-TS pattern \u2014 prefer 'as const' for a frozen object literal`,
41840
+ line,
41841
+ column: match[0].indexOf("enum") + 1,
41842
+ advice: 'Replace `enum Foo { A, B }` with `const Foo = { A: "A", B: "B" } as const` (or `const Foo = ["A", "B"] as const`). Modern TS style guides (Google, TS-eslint) prefer `as const` because enums have surprising runtime semantics. Reference: ts/enum-vs-as-const v0.19.'
41843
+ });
41844
+ }
41845
+ return issues;
41846
+ }
41847
+ });
41848
+ }
41849
+ });
41850
+
41851
+ // src/rules/ts/excessive-type-assertion.ts
41852
+ var DEFAULT_MAX, FN_DECL_REGEX, AS_REGEX, tsExcessiveTypeAssertionRule;
41853
+ var init_excessive_type_assertion = __esm({
41854
+ "src/rules/ts/excessive-type-assertion.ts"() {
41855
+ "use strict";
41856
+ init_rule();
41857
+ DEFAULT_MAX = 3;
41858
+ FN_DECL_REGEX = /^[ \t]*(?:export\s+)?(?:async\s+)?function\s+[A-Za-z_$][\w$]*\s*\([^)]*\)\s*[^{]*\{|^\s*(?:export\s+)?(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*[^=]+)?\s*=>\s*\{|^\s*(?:public|private|protected|static|async|abstract|readonly|\s)*\s*[A-Za-z_$][\w$]*\s*\([^)]*\)\s*:\s*[^{]*\{/gm;
41859
+ AS_REGEX = /(?<![\w$])as\s+(?!const)([A-Z][\w$.,<>\[\]|&]*)/g;
41860
+ tsExcessiveTypeAssertionRule = createRule({
41861
+ id: "ts/excessive-type-assertion",
41862
+ category: "typo",
41863
+ severity: "low",
41864
+ aiSpecific: true,
41865
+ description: "Function with >3 `as` type assertions \u2014 likely AI fighting the type system",
41866
+ create(_context) {
41867
+ return { maxAssertionsPerFunction: DEFAULT_MAX };
41868
+ },
41869
+ analyze(context, facts) {
41870
+ const issues = [];
41871
+ const source = facts.v2?._source;
41872
+ if (!source) return issues;
41873
+ let match;
41874
+ FN_DECL_REGEX.lastIndex = 0;
41875
+ while ((match = FN_DECL_REGEX.exec(source)) !== null) {
41876
+ const startIdx = match.index;
41877
+ const openBraceIdx = source.indexOf("{", startIdx);
41878
+ if (openBraceIdx < 0) continue;
41879
+ let depth = 1;
41880
+ let i = openBraceIdx + 1;
41881
+ while (i < source.length && depth > 0) {
41882
+ const ch = source[i];
41883
+ if (ch === "{") depth++;
41884
+ else if (ch === "}") depth--;
41885
+ i++;
41886
+ }
41887
+ const body = source.slice(openBraceIdx, i);
41888
+ const line = source.slice(0, startIdx).split("\n").length;
41889
+ let asCount = 0;
41890
+ const seen = /* @__PURE__ */ new Set();
41891
+ let asMatch;
41892
+ AS_REGEX.lastIndex = 0;
41893
+ while ((asMatch = AS_REGEX.exec(body)) !== null) {
41894
+ const captured = asMatch[1];
41895
+ if (seen.has(captured)) continue;
41896
+ seen.add(captured);
41897
+ asCount++;
41898
+ }
41899
+ if (asCount > context.maxAssertionsPerFunction) {
41900
+ issues.push({
41901
+ ruleId: "ts/excessive-type-assertion",
41902
+ category: "typo",
41903
+ severity: "low",
41904
+ aiSpecific: true,
41905
+ message: `Function has ${asCount} 'as' assertions (max ${context.maxAssertionsPerFunction}) \u2014 likely AI fighting the type system`,
41906
+ line,
41907
+ column: 1,
41908
+ advice: "More than 3 `as` assertions in a function is a strong signal that the type is wrong, not the code. Fix the type definition (or use a type guard) instead of bypassing the type system. Reference: ts/excessive-type-assertion v0.19."
41909
+ });
41910
+ }
41911
+ }
41912
+ return issues;
41913
+ }
41914
+ });
41915
+ }
41916
+ });
41917
+
41918
+ // src/rules/ts/import-type-misuse.ts
41919
+ var INLINE_TYPE_IMPORT_REGEX, tsImportTypeMisuseRule;
41920
+ var init_import_type_misuse = __esm({
41921
+ "src/rules/ts/import-type-misuse.ts"() {
41922
+ "use strict";
41923
+ init_rule();
41924
+ INLINE_TYPE_IMPORT_REGEX = /^[ \t]*import\s*\{[^}]*\btype\s+[A-Za-z_]/gm;
41925
+ tsImportTypeMisuseRule = createRule({
41926
+ id: "ts/import-type-misuse",
41927
+ category: "typo",
41928
+ severity: "low",
41929
+ aiSpecific: true,
41930
+ description: "Inline `import { type X }` \u2014 prefer `import type { X }` for clarity",
41931
+ create(_context) {
41932
+ return {};
41933
+ },
41934
+ analyze(_context, facts) {
41935
+ const issues = [];
41936
+ const source = facts.v2?._source;
41937
+ if (!source) return issues;
41938
+ let match;
41939
+ INLINE_TYPE_IMPORT_REGEX.lastIndex = 0;
41940
+ while ((match = INLINE_TYPE_IMPORT_REGEX.exec(source)) !== null) {
41941
+ const line = source.slice(0, match.index).split("\n").length;
41942
+ issues.push({
41943
+ ruleId: "ts/import-type-misuse",
41944
+ category: "typo",
41945
+ severity: "low",
41946
+ aiSpecific: true,
41947
+ message: "Inline `type` in a value import \u2014 split into a separate `import type` statement",
41948
+ line,
41949
+ column: match[0].indexOf("type") + 1,
41950
+ advice: 'Use `import type { X } from "..."` instead of `import { type X } from "..."`. The inline form is valid but the split form is more common in real codebases and makes the type-only intent unambiguous. Reference: ts/import-type-misuse v0.19.'
41951
+ });
41952
+ }
41953
+ return issues;
41954
+ }
41955
+ });
41956
+ }
41957
+ });
41958
+
41959
+ // src/rules/ts/never-vs-unknown.ts
41960
+ var NEVER_RETURN_REGEX, THROW_OR_LOOP_REGEX, tsNeverVsUnknownRule;
41961
+ var init_never_vs_unknown = __esm({
41962
+ "src/rules/ts/never-vs-unknown.ts"() {
41963
+ "use strict";
41964
+ init_rule();
41965
+ NEVER_RETURN_REGEX = /^[ \t]*(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)[^{]*:\s*(?:Promise<)?never\b[^{]*\{/gm;
41966
+ THROW_OR_LOOP_REGEX = /\b(?:throw\b|while\s*\(|for\s*\(|process\.exit\b|System\.exit\b)/;
41967
+ tsNeverVsUnknownRule = createRule({
41968
+ id: "ts/never-vs-unknown",
41969
+ category: "typo",
41970
+ severity: "low",
41971
+ aiSpecific: true,
41972
+ description: "Return type `: never` but body has no throw / loop / exit \u2014 likely AI misuse",
41973
+ create(_context) {
41974
+ return {};
41975
+ },
41976
+ analyze(_context, facts) {
41977
+ const issues = [];
41978
+ const source = facts.v2?._source;
41979
+ if (!source) return issues;
41980
+ let match;
41981
+ NEVER_RETURN_REGEX.lastIndex = 0;
41982
+ while ((match = NEVER_RETURN_REGEX.exec(source)) !== null) {
41983
+ const fnName = match[1];
41984
+ const startIdx = match.index;
41985
+ const line = source.slice(0, startIdx).split("\n").length;
41986
+ const openBraceIdx = source.indexOf("{", startIdx);
41987
+ if (openBraceIdx < 0) continue;
41988
+ let depth = 1;
41989
+ let i = openBraceIdx + 1;
41990
+ while (i < source.length && depth > 0) {
41991
+ const ch = source[i];
41992
+ if (ch === "{") depth++;
41993
+ else if (ch === "}") depth--;
41994
+ i++;
41995
+ }
41996
+ const body = source.slice(openBraceIdx, i);
41997
+ if (THROW_OR_LOOP_REGEX.test(body)) continue;
41998
+ issues.push({
41999
+ ruleId: "ts/never-vs-unknown",
42000
+ category: "typo",
42001
+ severity: "low",
42002
+ aiSpecific: true,
42003
+ message: `Function '${fnName}' returns 'never' but its body has no throw, loop, or exit \u2014 likely AI misuse`,
42004
+ line,
42005
+ column: match[0].indexOf("never") + 1,
42006
+ advice: 'The `never` return type means "this function never returns". Reserve it for functions that always throw, always loop, or always exit. For "impossible" branches, use a concrete type (`void`, `Error`, `unknown`) and an exhaustive check. Reference: ts/never-vs-unknown v0.19.'
42007
+ });
42008
+ }
42009
+ return issues;
42010
+ }
42011
+ });
42012
+ }
42013
+ });
42014
+
42015
+ // src/rules/ts/optional-chain-overuse.ts
42016
+ var DEFAULT_MIN_CHAIN_LENGTH, tsOptionalChainOveruseRule;
42017
+ var init_optional_chain_overuse = __esm({
42018
+ "src/rules/ts/optional-chain-overuse.ts"() {
42019
+ "use strict";
42020
+ init_rule();
42021
+ DEFAULT_MIN_CHAIN_LENGTH = 5;
42022
+ tsOptionalChainOveruseRule = createRule({
42023
+ id: "ts/optional-chain-overuse",
42024
+ category: "logic",
42025
+ severity: "low",
42026
+ aiSpecific: true,
42027
+ description: "Optional chaining (?.) used 5+ times in a single chain \u2014 AI tends to chain rather than narrow",
42028
+ create(_context) {
42029
+ return { minChainLength: DEFAULT_MIN_CHAIN_LENGTH };
42030
+ },
42031
+ analyze(context, facts) {
42032
+ const issues = [];
42033
+ const expressions = facts.v2.logic?.logicalExpressions;
42034
+ if (!expressions) return issues;
42035
+ for (const expression of expressions) {
42036
+ if (expression.depth >= context.minChainLength && expression.isOptionalChainLike) {
42037
+ issues.push({
42038
+ ruleId: "ts/optional-chain-overuse",
42039
+ category: "logic",
42040
+ severity: "low",
42041
+ aiSpecific: true,
42042
+ message: `Optional chain depth ${expression.depth} \u2014 break with an intermediate variable or guard clause`,
42043
+ line: expression.line,
42044
+ column: expression.column,
42045
+ advice: "Long optional chains are an AI pattern. Use a guard clause (`if (!value) return`) or intermediate variables to make the narrowing explicit. Reference: ts/optional-chain-overuse v0.19."
42046
+ });
42047
+ }
42048
+ }
42049
+ return issues;
42050
+ }
42051
+ });
42052
+ }
42053
+ });
42054
+
40909
42055
  // src/rules/typo/calc-fontsize.ts
40910
42056
  var FONT_SIZE_RE, calcFontsizeRule;
40911
42057
  var init_calc_fontsize = __esm({
@@ -42010,7 +43156,7 @@ function spanEnd(node) {
42010
43156
  }
42011
43157
  return void 0;
42012
43158
  }
42013
- function buildLineOffsets(source) {
43159
+ function buildLineOffsets2(source) {
42014
43160
  const offsets = [0];
42015
43161
  let byteOffset = 0;
42016
43162
  for (let i = 0; i < source.length; i++) {
@@ -44011,9 +45157,60 @@ function buildV2Facts(facts, source, ext, framework, config, templateClassNames
44011
45157
  })),
44012
45158
  disabledRules: extractDisabledRules(source),
44013
45159
  templateClassNames,
45160
+ // v0.18.9 — populate the Rust AST record when the file is `.rs`.
45161
+ // Calling `parseRustFile` here keeps the dead-code detector's
45162
+ // import-binding pass a pure function over the same source the
45163
+ // walker saw. The native-binding guard lives inside
45164
+ // `parseRustFile` (returns an empty record when tree-sitter is
45165
+ // unavailable).
45166
+ rustFile: buildRustFileRecord(facts.filePath, source),
44014
45167
  _source: source
44015
45168
  };
44016
45169
  }
45170
+ function buildRustFileRecord(filePath, source) {
45171
+ if (!filePath.toLowerCase().endsWith(".rs")) return void 0;
45172
+ const structure = parseRustFile(filePath, source);
45173
+ return {
45174
+ imports: structure.imports.map((i) => ({
45175
+ path: i.path,
45176
+ names: i.names.map((n) => ({ name: n.name, ...n.alias ? { alias: n.alias } : {} })),
45177
+ isGlob: i.isGlob,
45178
+ line: i.line,
45179
+ column: i.column
45180
+ })),
45181
+ functions: structure.functions.map((f) => ({
45182
+ name: f.name,
45183
+ line: f.line,
45184
+ column: f.column,
45185
+ isPublic: f.isPublic,
45186
+ isMethod: f.isMethod,
45187
+ ...f.receiver ? { receiver: f.receiver } : {},
45188
+ bodyLines: f.bodyLines,
45189
+ inTestConfig: f.inTestConfig
45190
+ })),
45191
+ structs: structure.structs.map((s) => ({
45192
+ name: s.name,
45193
+ line: s.line,
45194
+ column: s.column,
45195
+ isPublic: s.isPublic,
45196
+ isDerive: s.isDerive,
45197
+ derives: [...s.derives]
45198
+ })),
45199
+ traits: structure.traits.map((t) => ({
45200
+ name: t.name,
45201
+ line: t.line,
45202
+ column: t.column,
45203
+ isPublic: t.isPublic
45204
+ })),
45205
+ impls: structure.impls.map((ip) => ({
45206
+ ...ip.trait ? { trait: ip.trait } : {},
45207
+ type: ip.type,
45208
+ methods: [...ip.methods],
45209
+ line: ip.line,
45210
+ column: ip.column
45211
+ }))
45212
+ };
45213
+ }
44017
45214
  function splitFilePath(filePath) {
44018
45215
  const baseName2 = filePath.split("/").pop() ?? filePath;
44019
45216
  const dotIdx = baseName2.lastIndexOf(".");
@@ -44028,6 +45225,7 @@ var init_v2_build = __esm({
44028
45225
  "src/engine/visitors/v2-build.ts"() {
44029
45226
  "use strict";
44030
45227
  init_scan_helpers();
45228
+ init_rust();
44031
45229
  TAILWIND_COLOR_RE = /^(?:bg|text|border|ring|from|to|via|fill|stroke)-([a-z]+-\d+|white|black|transparent|current|\[.+?\])$/;
44032
45230
  TAILWIND_SPACING_RE = /^(?:[pm][xytrbl]?|gap|space-[xy])-(\d+(?:\.\d+)?)$/;
44033
45231
  TAILWIND_RADIUS_RE = /^(?:rounded(?:-[a-z]+)?)-(.+)$/;
@@ -44060,7 +45258,7 @@ function mergeTemplateClassNames(filePath, source, facts, templateClassNames) {
44060
45258
  }
44061
45259
  }
44062
45260
  function extractFacts(filePath, ast, source, supportsRsc = true, framework = "react", config) {
44063
- const lineOffsets = buildLineOffsets(source);
45261
+ const lineOffsets = buildLineOffsets2(source);
44064
45262
  const isTypeScript = /\.tsx?$/i.test(filePath);
44065
45263
  const NON_JSX_FRAMEWORKS = /* @__PURE__ */ new Set(["vue", "svelte", "astro"]);
44066
45264
  const facts = {
@@ -44348,7 +45546,7 @@ var init_naturalness_anomaly = __esm({
44348
45546
  const issues = [];
44349
45547
  const source = facts.v2?._source;
44350
45548
  if (!source) return issues;
44351
- const lineOffsets = buildLineOffsets(source);
45549
+ const lineOffsets = buildLineOffsets2(source);
44352
45550
  const components = facts.v2?.components ?? [];
44353
45551
  const perComponentMetrics = components.filter((c) => c.loc > 0).map(
44354
45552
  (c) => computeNaturalnessForRange(source, lineOffsets, c.line, c.line + c.loc - 1, context.model)
@@ -44995,6 +46193,10 @@ var init_builtins = __esm({
44995
46193
  init_expired_code_example();
44996
46194
  init_stale_function_reference();
44997
46195
  init_stale_package_reference();
46196
+ init_identical_block();
46197
+ init_error_wrap_without_context();
46198
+ init_nil_slice_vs_empty();
46199
+ init_struct_tag_inconsistency();
44998
46200
  init_gap_monopoly();
44999
46201
  init_math_element_uniformity();
45000
46202
  init_math_grid_uniformity();
@@ -45004,7 +46206,6 @@ var init_builtins = __esm({
45004
46206
  init_ghost_defensive();
45005
46207
  init_heaps_deviation();
45006
46208
  init_key_prop_missing();
45007
- init_ks_distribution_shift();
45008
46209
  init_math_any_density();
45009
46210
  init_math_console_log_storm();
45010
46211
  init_math_gini_class_usage();
@@ -45019,6 +46220,10 @@ var init_builtins = __esm({
45019
46220
  init_halstead_anomaly();
45020
46221
  init_terminology_drift();
45021
46222
  init_ux_pattern_fragmentation();
46223
+ init_stringly_typed();
46224
+ init_todo_macro();
46225
+ init_unused_pub_fn();
46226
+ init_unwrap_in_production();
45022
46227
  init_dangerous_cors();
45023
46228
  init_eval();
45024
46229
  init_exposed_env_var();
@@ -45034,6 +46239,11 @@ var init_builtins = __esm({
45034
46239
  init_fake_placeholder();
45035
46240
  init_missing_edge_case();
45036
46241
  init_weak_assertion();
46242
+ init_enum_vs_as_const();
46243
+ init_excessive_type_assertion();
46244
+ init_import_type_misuse();
46245
+ init_never_vs_unknown();
46246
+ init_optional_chain_overuse();
45037
46247
  init_calc_fontsize();
45038
46248
  init_calc_raw_px();
45039
46249
  init_clamp_offscale();
@@ -45096,6 +46306,10 @@ var init_builtins = __esm({
45096
46306
  expiredCodeExampleRule,
45097
46307
  staleFunctionReferenceRule,
45098
46308
  stalePackageReferenceRule,
46309
+ dupIdenticalBlockRule,
46310
+ goErrorWrapWithoutContextRule,
46311
+ goNilSliceVsEmptyRule,
46312
+ goStructTagInconsistencyRule,
45099
46313
  gapMonopolyRule,
45100
46314
  mathElementUniformityRule,
45101
46315
  mathGridUniformityRule,
@@ -45105,7 +46319,6 @@ var init_builtins = __esm({
45105
46319
  ghostDefensiveRule,
45106
46320
  heapsDeviationRule,
45107
46321
  keyPropMissingRule,
45108
- ksDistributionShiftRule,
45109
46322
  mathAnyDensityRule,
45110
46323
  mathConsoleLogStormRule,
45111
46324
  mathGiniClassUsageRule,
@@ -45120,6 +46333,10 @@ var init_builtins = __esm({
45120
46333
  halsteadAnomalyRule,
45121
46334
  terminologyDriftRule,
45122
46335
  uxPatternFragmentationRule,
46336
+ rustStringlyTypedRule,
46337
+ rustTodoMacroRule,
46338
+ rustUnusedPubFnRule,
46339
+ rustUnwrapInProductionRule,
45123
46340
  dangerousCorsRule,
45124
46341
  evalRule,
45125
46342
  exposedEnvVarRule,
@@ -45135,6 +46352,11 @@ var init_builtins = __esm({
45135
46352
  fakePlaceholderRule,
45136
46353
  missingEdgeCaseRule,
45137
46354
  weakAssertionRule,
46355
+ tsEnumVsAsConstRule,
46356
+ tsExcessiveTypeAssertionRule,
46357
+ tsImportTypeMisuseRule,
46358
+ tsNeverVsUnknownRule,
46359
+ tsOptionalChainOveruseRule,
45138
46360
  calcFontsizeRule,
45139
46361
  calcRawPxRule,
45140
46362
  clampOffscaleRule,
@@ -47097,6 +48319,22 @@ var init_registry = __esm({
47097
48319
  if (!filter) return list;
47098
48320
  return list.filter((r) => filter.kind === "ai" ? r.aiSpecific : !r.aiSpecific);
47099
48321
  }
48322
+ /** v0.18.8: remove every rule where `predicate(rule)` returns true.
48323
+ * Used by focused calibration scripts to scan a single category
48324
+ * without instantiating all 99 rules. */
48325
+ removeWhere(predicate) {
48326
+ let removed = 0;
48327
+ for (const [id, rule] of this.rules) {
48328
+ if (predicate(rule)) {
48329
+ this.rules.delete(id);
48330
+ removed++;
48331
+ }
48332
+ }
48333
+ return removed;
48334
+ }
48335
+ all() {
48336
+ return Array.from(this.rules.values());
48337
+ }
47100
48338
  createContexts(config, filePath, cwd, hotspotIssues = []) {
47101
48339
  const context = {
47102
48340
  config,
@@ -47122,1044 +48360,1943 @@ var signal_strength_default;
47122
48360
  var init_signal_strength = __esm({
47123
48361
  "src/rules/signal-strength.json"() {
47124
48362
  signal_strength_default = {
47125
- "logic/math-console-log-storm": {
47126
- recall: 7e-3,
47127
- fpRate: 1e-3,
47128
- ratio: 6.71,
47129
- precision: 0.8968,
47130
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48363
+ "ai/any-density": {
48364
+ recall: 6e-3,
48365
+ fpRate: 37e-4,
48366
+ ratio: 175.31,
48367
+ precision: 0.6523,
48368
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47131
48369
  verdict: "USEFUL",
47132
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=1678, FP=193, P=89.7%, FPR=0.10%, lift=6.7. aiSpecific=True.",
47133
- aiSpecific: true
48370
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1760, FP=938, P=65.2%, FPR=0.37%, lift=175.31. v7 was USEFUL (TP=1313, FP=758, lift=153.41). v8 was USEFUL (TP=447, FP=180).",
48371
+ aiSpecific: true,
48372
+ _v7Verdict: "USEFUL",
48373
+ _v7Lift: 153.41,
48374
+ _v7Recall: 55e-4,
48375
+ _v7FpRate: 41e-4,
48376
+ _v7Precision: 0.634,
48377
+ _v8Verdict: "USEFUL",
48378
+ _v8Lift: 271.97
47134
48379
  },
47135
- "logic/math-any-density": {
47136
- recall: 16e-4,
47137
- fpRate: 13e-4,
47138
- ratio: 1.17,
47139
- precision: 0.6035,
47140
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47141
- verdict: "NOISY",
47142
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. NOISY \u2014 TP=376, FP=247, P=60.4%, FPR=0.13%, lift=1.2. aiSpecific=True.",
47143
- defaultOff: true,
47144
- aiSpecific: true
48380
+ "ai/comment-ratio": {
48381
+ recall: 0.2771,
48382
+ fpRate: 0.1578,
48383
+ ratio: 4.26,
48384
+ precision: 0.6721,
48385
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48386
+ verdict: "USEFUL",
48387
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=81513, FP=39766, P=67.2%, FPR=15.78%, lift=4.26. v7 was USEFUL (TP=60308, FP=29876, lift=4.11). v8 was USEFUL (TP=21205, FP=9890).",
48388
+ aiSpecific: true,
48389
+ _v7Verdict: "USEFUL",
48390
+ _v7Lift: 4.11,
48391
+ _v7Recall: 0.2545,
48392
+ _v7FpRate: 0.1629,
48393
+ _v7Precision: 0.6687,
48394
+ _v8Verdict: "USEFUL",
48395
+ _v8Lift: 4.73
47145
48396
  },
47146
- "logic/boundary-violation": {
47147
- recall: 0.0263,
47148
- fpRate: 0.0175,
47149
- ratio: 1.5,
47150
- precision: 0.6601,
47151
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47152
- verdict: "HYGIENE",
47153
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=6282, FP=3235, P=66.0%, FPR=1.75%, lift=1.5. aiSpecific=False.",
47154
- aiSpecific: false
48397
+ "ai/compression-profile": {
48398
+ recall: 0.3478,
48399
+ fpRate: 0.1488,
48400
+ ratio: 4.92,
48401
+ precision: 0.7318,
48402
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48403
+ verdict: "USEFUL",
48404
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=102328, FP=37511, P=73.2%, FPR=14.88%, lift=4.92. v7 was USEFUL (TP=75031, FP=27473, lift=4.89). v8 was USEFUL (TP=27297, FP=10038).",
48405
+ aiSpecific: true,
48406
+ _v7Verdict: "USEFUL",
48407
+ _v7Lift: 4.89,
48408
+ _v7Recall: 0.3166,
48409
+ _v7FpRate: 0.1498,
48410
+ _v7Precision: 0.732,
48411
+ _v8Verdict: "USEFUL",
48412
+ _v8Lift: 5
47155
48413
  },
47156
- "logic/reactive-hook-soup": {
47157
- recall: 38e-4,
48414
+ "ai/console-debug-storm": {
48415
+ recall: 73e-4,
47158
48416
  fpRate: 9e-4,
47159
- ratio: 4.29,
47160
- precision: 0.8476,
47161
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48417
+ ratio: 949.21,
48418
+ precision: 0.9,
48419
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47162
48420
  verdict: "USEFUL",
47163
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=901, FP=162, P=84.8%, FPR=0.09%, lift=4.3. aiSpecific=True.",
47164
- aiSpecific: true
48421
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=2150, FP=239, P=90.0%, FPR=0.09%, lift=949.21. v7 was USEFUL (TP=1912, FP=175, lift=960.19). v8 was USEFUL (TP=238, FP=64).",
48422
+ aiSpecific: true,
48423
+ _v7Verdict: "USEFUL",
48424
+ _v7Lift: 960.19,
48425
+ _v7Recall: 81e-4,
48426
+ _v7FpRate: 1e-3,
48427
+ _v7Precision: 0.9161,
48428
+ _v8Verdict: "USEFUL",
48429
+ _v8Lift: 845.55
47165
48430
  },
47166
- "logic/optimistic-no-rollback": {
47167
- recall: 12e-4,
47168
- fpRate: 3e-4,
47169
- ratio: 3.61,
47170
- precision: 0.824,
47171
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48431
+ "ai/default-react-stack": {
48432
+ recall: 1e-3,
48433
+ fpRate: 0,
48434
+ ratio: 251225.49,
48435
+ precision: 0.9966,
48436
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47172
48437
  verdict: "USEFUL",
47173
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=281, FP=60, P=82.4%, FPR=0.03%, lift=3.6. aiSpecific=True.",
47174
- aiSpecific: true
48438
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=294, FP=1, P=99.7%, FPR=0.00%, lift=251225.49. v7 was USEFUL (TP=231, FP=1, lift=182622.43). v8 was USEFUL (TP=63, FP=0). v0.19 default-on (explicit defaultOff: false): P 99.7% / 251k lift \u2014 core AI fingerprint.",
48439
+ aiSpecific: true,
48440
+ _v7Verdict: "USEFUL",
48441
+ _v7Lift: 182622.43,
48442
+ _v7Recall: 1e-3,
48443
+ _v7FpRate: 0,
48444
+ _v7Precision: 0.9957,
48445
+ _v8Verdict: "USEFUL",
48446
+ _v8Lift: 99999,
48447
+ defaultOff: false
47175
48448
  },
47176
- "logic/zombie-state": {
47177
- recall: 1e-4,
47178
- fpRate: 0,
47179
- ratio: 9.26,
47180
- precision: 0.9231,
47181
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48449
+ "ai/errors-near-eof": {
48450
+ recall: 0.0948,
48451
+ fpRate: 0.052,
48452
+ ratio: 13.09,
48453
+ precision: 0.6803,
48454
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47182
48455
  verdict: "USEFUL",
47183
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=24, FP=2, P=92.3%, FPR=0.00%, lift=9.3. aiSpecific=True.",
47184
- aiSpecific: true
48456
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=27874, FP=13097, P=68.0%, FPR=5.20%, lift=13.09. v7 was USEFUL (TP=16673, FP=10112, lift=11.29). v8 was USEFUL (TP=11201, FP=2985).",
48457
+ aiSpecific: true,
48458
+ _v7Verdict: "USEFUL",
48459
+ _v7Lift: 11.29,
48460
+ _v7Recall: 0.0704,
48461
+ _v7FpRate: 0.0551,
48462
+ _v7Precision: 0.6225,
48463
+ _v8Verdict: "USEFUL",
48464
+ _v8Lift: 18.16
47185
48465
  },
47186
- "logic/math-gini-class-usage": {
47187
- recall: 13e-4,
48466
+ "ai/fetch-default-overuse": {
48467
+ recall: 27e-4,
47188
48468
  fpRate: 3e-4,
47189
- ratio: 5.09,
47190
- precision: 0.8683,
47191
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48469
+ ratio: 2679.84,
48470
+ precision: 0.9036,
48471
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47192
48472
  verdict: "USEFUL",
47193
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=310, FP=47, P=86.8%, FPR=0.03%, lift=5.1. aiSpecific=True.",
47194
- aiSpecific: true
48473
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=797, FP=85, P=90.4%, FPR=0.03%, lift=2679.84. v7 was USEFUL (TP=666, FP=76, lift=2166.14). v8 was USEFUL (TP=131, FP=9).",
48474
+ aiSpecific: true,
48475
+ _v7Verdict: "USEFUL",
48476
+ _v7Lift: 2166.14,
48477
+ _v7Recall: 28e-4,
48478
+ _v7FpRate: 4e-4,
48479
+ _v7Precision: 0.8976,
48480
+ _v8Verdict: "USEFUL",
48481
+ _v8Lift: 7139.19
47195
48482
  },
47196
- "visual/math-color-cluster": {
47197
- recall: 2e-4,
48483
+ "ai/library-reinvention": {
48484
+ recall: 3e-4,
47198
48485
  fpRate: 0,
47199
- ratio: 8.95,
47200
- precision: 0.9206,
47201
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47202
- verdict: "USEFUL",
47203
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=58, FP=5, P=92.1%, FPR=0.00%, lift=9.0. aiSpecific=True.",
47204
- aiSpecific: true
47205
- },
47206
- "visual/math-default-font": {
47207
- recall: 13e-4,
47208
- fpRate: 4e-4,
47209
- ratio: 3.4,
47210
- precision: 0.8151,
47211
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48486
+ ratio: 47415.05,
48487
+ precision: 0.9405,
48488
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47212
48489
  verdict: "USEFUL",
47213
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=313, FP=71, P=81.5%, FPR=0.04%, lift=3.4. aiSpecific=True.",
47214
- aiSpecific: true
48490
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=79, FP=5, P=94.0%, FPR=0.00%, lift=47415.05. v7 was USEFUL (TP=64, FP=5, lift=34024.44). v8 was USEFUL (TP=15, FP=0).",
48491
+ aiSpecific: true,
48492
+ _v7Verdict: "USEFUL",
48493
+ _v7Lift: 34024.44,
48494
+ _v7Recall: 3e-4,
48495
+ _v7FpRate: 0,
48496
+ _v7Precision: 0.9275,
48497
+ _v8Verdict: "USEFUL",
48498
+ _v8Lift: 99999
47215
48499
  },
47216
- "visual/math-gradient-hue-rotation": {
48500
+ "ai/log-rank-histogram": {
47217
48501
  recall: 0,
47218
48502
  fpRate: 0,
47219
48503
  ratio: 0,
47220
48504
  precision: 0,
47221
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48505
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47222
48506
  verdict: "DORMANT",
47223
- defaultOff: true,
47224
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Munsell, A. H. (1905), *A Color Notation*, Munsell Color Company; Itten, J. (1961), *The Art of Color*, Van Nostrand Reinhold. (Munsell color space + Itten color wheel \u2014 hue rotation analysis.)",
47225
- aiSpecific: true
48507
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=OK, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48508
+ aiSpecific: true,
48509
+ _v7Verdict: "OK",
48510
+ _v7Lift: 0,
48511
+ _v7Recall: 0,
48512
+ _v7FpRate: 0,
48513
+ _v7Precision: 0,
48514
+ _v8Verdict: "DORMANT",
48515
+ _v8Lift: 1,
48516
+ defaultOff: true
47226
48517
  },
47227
- "visual/math-rounded-entropy": {
47228
- recall: 37e-4,
47229
- fpRate: 3e-4,
47230
- ratio: 10.9,
47231
- precision: 0.9339,
47232
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47233
- verdict: "USEFUL",
47234
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=876, FP=62, P=93.4%, FPR=0.03%, lift=10.9. aiSpecific=True.",
47235
- aiSpecific: true
48518
+ "ai/markdown-leakage": {
48519
+ recall: 0,
48520
+ fpRate: 0,
48521
+ ratio: 10003.17,
48522
+ precision: 0.3571,
48523
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48524
+ verdict: "OK",
48525
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=5, FP=9, P=35.7%, FPR=0.00%, lift=10003.17. v7 was USEFUL (TP=5, FP=2, lift=65504.64). v8 was INVERTED (TP=0, FP=7).",
48526
+ aiSpecific: true,
48527
+ _v7Verdict: "USEFUL",
48528
+ _v7Lift: 65504.64,
48529
+ _v7Recall: 0,
48530
+ _v7FpRate: 0,
48531
+ _v7Precision: 0.7143,
48532
+ _v8Verdict: "INVERTED",
48533
+ _v8Lift: 0
47236
48534
  },
47237
- "visual/math-font-entropy": {
47238
- recall: 45e-4,
47239
- fpRate: 13e-4,
47240
- ratio: 3.32,
47241
- precision: 0.8114,
47242
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48535
+ "ai/renyi-profile": {
48536
+ recall: 0,
48537
+ fpRate: 0,
48538
+ ratio: 5251.67,
48539
+ precision: 0.25,
48540
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48541
+ verdict: "OK",
48542
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=4, FP=12, P=25.0%, FPR=0.00%, lift=5251.67. v7 was OK (TP=3, FP=9, lift=5094.81). v8 was OK (TP=1, FP=3).",
48543
+ aiSpecific: true,
48544
+ _v7Verdict: "OK",
48545
+ _v7Lift: 5094.81,
48546
+ _v7Recall: 0,
48547
+ _v7FpRate: 0,
48548
+ _v7Precision: 0.25,
48549
+ _v8Verdict: "OK",
48550
+ _v8Lift: 5722.25
48551
+ },
48552
+ "ai/segment-surprisal-cv": {
48553
+ recall: 0.2019,
48554
+ fpRate: 0.0787,
48555
+ ratio: 9.52,
48556
+ precision: 0.7495,
48557
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47243
48558
  verdict: "USEFUL",
47244
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=1067, FP=248, P=81.1%, FPR=0.13%, lift=3.3. aiSpecific=True.",
47245
- aiSpecific: true
48559
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=59390, FP=19849, P=75.0%, FPR=7.87%, lift=9.52. v7 was USEFUL (TP=43498, FP=14983, lift=9.11). v8 was USEFUL (TP=15892, FP=4866).",
48560
+ aiSpecific: true,
48561
+ _v7Verdict: "USEFUL",
48562
+ _v7Lift: 9.11,
48563
+ _v7Recall: 0.1836,
48564
+ _v7FpRate: 0.0817,
48565
+ _v7Precision: 0.7438,
48566
+ _v8Verdict: "USEFUL",
48567
+ _v8Lift: 10.8
47246
48568
  },
47247
- "visual/math-spacing-entropy": {
47248
- recall: 18e-4,
48569
+ "ai/state-default-overuse": {
48570
+ recall: 3e-3,
47249
48571
  fpRate: 6e-4,
47250
- ratio: 3.01,
47251
- precision: 0.7959,
47252
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48572
+ ratio: 1315.06,
48573
+ precision: 0.8451,
48574
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47253
48575
  verdict: "USEFUL",
47254
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=425, FP=109, P=79.6%, FPR=0.06%, lift=3.0. aiSpecific=True.",
47255
- aiSpecific: true
48576
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=884, FP=162, P=84.5%, FPR=0.06%, lift=1315.06. v7 was USEFUL (TP=701, FP=158, lift=947.32). v8 was USEFUL (TP=183, FP=4).",
48577
+ aiSpecific: true,
48578
+ _v7Verdict: "USEFUL",
48579
+ _v7Lift: 947.32,
48580
+ _v7Recall: 3e-3,
48581
+ _v7FpRate: 9e-4,
48582
+ _v7Precision: 0.8161,
48583
+ _v8Verdict: "USEFUL",
48584
+ _v8Lift: 16799.55
47256
48585
  },
47257
- "visual/clamp-soup": {
48586
+ "ai/tailwind-color-overuse": {
48587
+ recall: 0.0264,
48588
+ fpRate: 38e-4,
48589
+ ratio: 230.97,
48590
+ precision: 0.8888,
48591
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48592
+ verdict: "USEFUL",
48593
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=7752, FP=970, P=88.9%, FPR=0.38%, lift=230.97. v7 was USEFUL (TP=5169, FP=958, lift=161.52). v8 was USEFUL (TP=2583, FP=12).",
48594
+ aiSpecific: true,
48595
+ _v7Verdict: "USEFUL",
48596
+ _v7Lift: 161.52,
48597
+ _v7Recall: 0.0218,
48598
+ _v7FpRate: 52e-4,
48599
+ _v7Precision: 0.8436,
48600
+ _v8Verdict: "USEFUL",
48601
+ _v8Lift: 5695.79
48602
+ },
48603
+ "ai/text-like-ratio": {
48604
+ recall: 0,
48605
+ fpRate: 0,
48606
+ ratio: 201664,
48607
+ precision: 0.8,
48608
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48609
+ verdict: "USEFUL",
48610
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=4, FP=1, P=80.0%, FPR=0.00%, lift=201664.00. v7 was USEFUL (TP=3, FP=1, lift=137559.75). v8 was USEFUL (TP=1, FP=0).",
48611
+ aiSpecific: true,
48612
+ _v7Verdict: "USEFUL",
48613
+ _v7Lift: 137559.75,
48614
+ _v7Recall: 0,
48615
+ _v7FpRate: 0,
48616
+ _v7Precision: 0.75,
48617
+ _v8Verdict: "USEFUL",
48618
+ _v8Lift: 99999
48619
+ },
48620
+ "ai/whitespace-regularity": {
48621
+ recall: 0.0733,
48622
+ fpRate: 0.0677,
48623
+ ratio: 8.25,
48624
+ precision: 0.5583,
48625
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48626
+ verdict: "USEFUL",
48627
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=21567, FP=17061, P=55.8%, FPR=6.77%, lift=8.25. v7 was USEFUL (TP=17466, FP=13438, lift=7.71). v8 was USEFUL (TP=4101, FP=3623).",
48628
+ aiSpecific: true,
48629
+ _v7Verdict: "USEFUL",
48630
+ _v7Lift: 7.71,
48631
+ _v7Recall: 0.0737,
48632
+ _v7FpRate: 0.0733,
48633
+ _v7Precision: 0.5652,
48634
+ _v8Verdict: "USEFUL",
48635
+ _v8Lift: 10.06
48636
+ },
48637
+ "arch/astro-island-leak": {
47258
48638
  recall: 0,
47259
48639
  fpRate: 0,
47260
48640
  ratio: 0,
47261
48641
  precision: 0,
47262
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48642
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47263
48643
  verdict: "DORMANT",
47264
- defaultOff: true,
47265
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: W3C (2023), CSS Values and Units Module Level 4, W3C CR-css-values-4-20231218. (W3C clamp() spec \u2014 overuse is a code-smell, not a feature.)",
47266
- aiSpecific: true
48644
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48645
+ aiSpecific: true,
48646
+ _v7Verdict: "DORMANT",
48647
+ _v7Lift: 0,
48648
+ _v7Recall: 0,
48649
+ _v7FpRate: 0,
48650
+ _v7Precision: 0,
48651
+ _v8Verdict: "DORMANT",
48652
+ _v8Lift: 1,
48653
+ defaultOff: true
47267
48654
  },
47268
48655
  "component/giant-component": {
47269
- recall: 0.0176,
47270
- fpRate: 77e-4,
47271
- ratio: 2.3,
47272
- precision: 0.7485,
47273
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48656
+ recall: 0.0181,
48657
+ fpRate: 59e-4,
48658
+ ratio: 132.31,
48659
+ precision: 0.7815,
48660
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47274
48661
  verdict: "USEFUL",
47275
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=4205, FP=1413, P=74.8%, FPR=0.77%, lift=2.3. aiSpecific=True.",
47276
- aiSpecific: true
48662
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=5326, FP=1489, P=78.2%, FPR=0.59%, lift=132.31. v7 was USEFUL (TP=4205, FP=1413, lift=97.16). v8 was USEFUL (TP=1121, FP=76).",
48663
+ aiSpecific: true,
48664
+ _v7Verdict: "USEFUL",
48665
+ _v7Lift: 97.16,
48666
+ _v7Recall: 0.0177,
48667
+ _v7FpRate: 77e-4,
48668
+ _v7Precision: 0.7485,
48669
+ _v8Verdict: "USEFUL",
48670
+ _v8Lift: 846.15
48671
+ },
48672
+ "component/multiple-components-per-file": {
48673
+ recall: 0.0751,
48674
+ fpRate: 0.0423,
48675
+ ratio: 15.96,
48676
+ precision: 0.6747,
48677
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48678
+ verdict: "USEFUL",
48679
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=22103, FP=10655, P=67.5%, FPR=4.23%, lift=15.96. v7 was USEFUL (TP=16288, FP=9589, lift=12.04). v8 was USEFUL (TP=5815, FP=1066).",
48680
+ aiSpecific: false,
48681
+ _v7Verdict: "USEFUL",
48682
+ _v7Lift: 12.04,
48683
+ _v7Recall: 0.0687,
48684
+ _v7FpRate: 0.0523,
48685
+ _v7Precision: 0.6294,
48686
+ _v8Verdict: "USEFUL",
48687
+ _v8Lift: 54.44
47277
48688
  },
47278
48689
  "component/shadcn-prop-mismatch": {
47279
- recall: 13e-4,
48690
+ recall: 16e-4,
47280
48691
  fpRate: 1e-4,
47281
- ratio: 10.1,
47282
- precision: 0.929,
47283
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48692
+ ratio: 9990.98,
48693
+ precision: 0.9512,
48694
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47284
48695
  verdict: "USEFUL",
47285
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=314, FP=24, P=92.9%, FPR=0.01%, lift=10.1. aiSpecific=True.",
47286
- aiSpecific: true
48696
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=468, FP=24, P=95.1%, FPR=0.01%, lift=9990.98. v7 was USEFUL (TP=314, FP=24, lift=7099.57). v8 was USEFUL (TP=154, FP=0). v0.19 default-on (explicit defaultOff: false): P 95.1% / 9990x lift \u2014 UI bug.",
48697
+ aiSpecific: true,
48698
+ _v7Verdict: "USEFUL",
48699
+ _v7Lift: 7099.57,
48700
+ _v7Recall: 13e-4,
48701
+ _v7FpRate: 1e-4,
48702
+ _v7Precision: 0.929,
48703
+ _v8Verdict: "USEFUL",
48704
+ _v8Lift: 99999,
48705
+ defaultOff: false
47287
48706
  },
47288
- "layout/math-grid-uniformity": {
47289
- recall: 3e-4,
47290
- fpRate: 2e-4,
47291
- ratio: 1.72,
47292
- precision: 0.6907,
47293
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47294
- verdict: "OK",
47295
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. OK \u2014 TP=67, FP=30, P=69.1%, FPR=0.02%, lift=1.7. aiSpecific=True.",
47296
- aiSpecific: true
47297
- },
47298
- "layout/math-element-uniformity": {
47299
- recall: 28e-4,
47300
- fpRate: 12e-4,
47301
- ratio: 2.28,
47302
- precision: 0.7475,
47303
- lastCalibratedAt: "2026-06-27T12:00:00Z",
48707
+ "context/import-path-mismatch": {
48708
+ recall: 0.0681,
48709
+ fpRate: 0.0167,
48710
+ ratio: 49.48,
48711
+ precision: 0.8263,
48712
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47304
48713
  verdict: "USEFUL",
47305
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=666, FP=225, P=74.7%, FPR=0.12%, lift=2.3. aiSpecific=True.",
47306
- aiSpecific: true
48714
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=20032, FP=4210, P=82.6%, FPR=1.67%, lift=49.48. v7 was USEFUL (TP=17672, FP=4202, lift=35.26). v8 was USEFUL (TP=2360, FP=8).",
48715
+ aiSpecific: false,
48716
+ _v7Verdict: "USEFUL",
48717
+ _v7Lift: 35.26,
48718
+ _v7Recall: 0.0746,
48719
+ _v7FpRate: 0.0229,
48720
+ _v7Precision: 0.8079,
48721
+ _v8Verdict: "USEFUL",
48722
+ _v8Lift: 8554.38
47307
48723
  },
47308
- "typo/math-button-label-uniformity": {
47309
- recall: 2e-4,
47310
- fpRate: 1e-4,
47311
- ratio: 1.36,
47312
- precision: 0.6379,
47313
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47314
- verdict: "HYGIENE",
47315
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=37, FP=21, P=63.8%, FPR=0.01%, lift=1.4. aiSpecific=False.",
47316
- aiSpecific: false
47317
- },
47318
- "perf/css-bloat": {
47319
- recall: 0.0117,
47320
- fpRate: 32e-4,
47321
- ratio: 3.64,
47322
- precision: 0.8252,
47323
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47324
- verdict: "HYGIENE",
47325
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=2790, FP=591, P=82.5%, FPR=0.32%, lift=3.6. aiSpecific=False.",
47326
- aiSpecific: false
47327
- },
47328
- "wcag/focus-appearance": {
47329
- recall: 26e-4,
47330
- fpRate: 1e-4,
47331
- ratio: 19.68,
47332
- precision: 0.9623,
47333
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47334
- verdict: "HYGIENE",
47335
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=612, FP=24, P=96.2%, FPR=0.01%, lift=19.7. aiSpecific=False.",
47336
- aiSpecific: false
47337
- },
47338
- "wcag/target-size": {
48724
+ "db/duplicate-index": {
47339
48725
  recall: 0,
47340
48726
  fpRate: 0,
47341
48727
  ratio: 0,
47342
48728
  precision: 0,
47343
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48729
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47344
48730
  verdict: "DORMANT",
47345
- defaultOff: true,
47346
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: W3C (2018), Web Content Accessibility Guidelines (WCAG) 2.1, Success Criterion 2.5.5 (Target Size); Fitts, P. M. (1954), \u2018The Information Capacity of the Human Motor System in Controlling the Amplitude of Movement\u2019, J. Exp. Psychol. 47(6):381-391. (WCAG 2.5.5 + Fitts's Law \u2014 minimum 24\xD724 CSS px tap target.)",
47347
- aiSpecific: false
47348
- },
47349
- "component/multiple-components-per-file": {
47350
- recall: 0.0681,
47351
- fpRate: 0.052,
47352
- ratio: 1.31,
47353
- precision: 0.6294,
47354
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47355
- verdict: "HYGIENE",
47356
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=16288, FP=9589, P=62.9%, FPR=5.20%, lift=1.3. aiSpecific=False.",
47357
- aiSpecific: false
47358
- },
47359
- "context/import-path-mismatch": {
47360
- recall: 0.0739,
47361
- fpRate: 0.0228,
47362
- ratio: 3.25,
47363
- precision: 0.8079,
47364
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47365
- verdict: "HYGIENE",
47366
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=17672, FP=4202, P=80.8%, FPR=2.28%, lift=3.2. aiSpecific=False.",
47367
- aiSpecific: false
47368
- },
47369
- "logic/key-prop-missing": {
47370
- recall: 17e-4,
47371
- fpRate: 14e-4,
47372
- ratio: 1.28,
47373
- precision: 0.6246,
47374
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47375
- verdict: "HYGIENE",
47376
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=416, FP=250, P=62.5%, FPR=0.14%, lift=1.3. aiSpecific=False.",
47377
- aiSpecific: false
47378
- },
47379
- "logic/math-variable-name-entropy": {
47380
- recall: 2e-4,
47381
- fpRate: 1e-4,
47382
- ratio: 1.07,
47383
- precision: 0.5806,
47384
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47385
- verdict: "HYGIENE",
47386
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=36, FP=26, P=58.1%, FPR=0.01%, lift=1.1. aiSpecific=False.",
47387
- aiSpecific: false
47388
- },
47389
- "security/public-admin-route": {
47390
- recall: 28e-4,
47391
- fpRate: 78e-4,
47392
- ratio: 0.4,
47393
- precision: 0.2251,
47394
- lastCalibratedAt: "2026-06-26T22:30:00Z",
47395
- verdict: "HYGIENE",
47396
- _calibrationNote: "v4 corpus (2026-06-25): 95,599 neg + 76,550 pos (frontend, TS/TSX/JS/JSX). INVERTED \u2014 TP=217, FP=747, P=22.5%, FPR=0.78%, lift=0.4. [v0.12.2: reclassified to HYGIENE because rule is aiSpecific: false]",
47397
- defaultOff: true,
47398
- aiSpecific: false
47399
- },
47400
- "security/unsafe-html-render": {
47401
- recall: 13e-4,
47402
- fpRate: 9e-4,
47403
- ratio: 1.5,
47404
- precision: 0.661,
47405
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47406
- verdict: "HYGIENE",
47407
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=312, FP=160, P=66.1%, FPR=0.09%, lift=1.5. aiSpecific=False.",
47408
- aiSpecific: false
47409
- },
47410
- "visual/naturalness-anomaly": {
47411
- recall: 0.1645,
47412
- fpRate: 0.0617,
47413
- ratio: 2.67,
47414
- precision: 0.7755,
47415
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47416
- verdict: "USEFUL",
47417
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=39319, FP=11382, P=77.6%, FPR=6.17%, lift=2.7. aiSpecific=True.",
47418
- aiSpecific: true
48731
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48732
+ aiSpecific: false,
48733
+ _v7Verdict: "DORMANT",
48734
+ _v7Lift: 0,
48735
+ _v7Recall: 0,
48736
+ _v7FpRate: 0,
48737
+ _v7Precision: 0,
48738
+ _v8Verdict: "DORMANT",
48739
+ _v8Lift: 1,
48740
+ defaultOff: true
47419
48741
  },
47420
- "perf/halstead-anomaly": {
48742
+ "db/enum-sprawl": {
47421
48743
  recall: 0,
47422
48744
  fpRate: 0,
47423
- ratio: 2.32,
47424
- precision: 0.75,
47425
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47426
- verdict: "USEFUL",
47427
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=3, FP=1, P=75.0%, FPR=0.00%, lift=2.3. aiSpecific=True.",
47428
- aiSpecific: true
48745
+ ratio: 0,
48746
+ precision: 0,
48747
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48748
+ verdict: "DORMANT",
48749
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48750
+ aiSpecific: false,
48751
+ _v7Verdict: "DORMANT",
48752
+ _v7Lift: 0,
48753
+ _v7Recall: 0,
48754
+ _v7FpRate: 0,
48755
+ _v7Precision: 0,
48756
+ _v8Verdict: "DORMANT",
48757
+ _v8Lift: 1,
48758
+ defaultOff: true
47429
48759
  },
47430
- "arch/astro-island-leak": {
48760
+ "db/missing-fk-index": {
47431
48761
  recall: 0,
47432
48762
  fpRate: 0,
47433
48763
  ratio: 0,
47434
48764
  precision: 0,
47435
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48765
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47436
48766
  verdict: "DORMANT",
47437
- defaultOff: true,
47438
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Hevery, M. (2022), \u2018Islands Architecture: A New Pattern for Server-Component Frameworks\u2019, ACM SIGPLAN International Conference on Object-Oriented Programming, Systems, Languages & Applications (OOPSLA), invited talk; Astro Documentation (2023), https://docs.astro.build. (Astro Islands architecture \u2014 client JS should not leak into server components.)",
47439
- aiSpecific: true
48767
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48768
+ aiSpecific: false,
48769
+ _v7Verdict: "DORMANT",
48770
+ _v7Lift: 0,
48771
+ _v7Recall: 0,
48772
+ _v7FpRate: 0,
48773
+ _v7Precision: 0,
48774
+ _v8Verdict: "DORMANT",
48775
+ _v8Lift: 1,
48776
+ defaultOff: true
47440
48777
  },
47441
- "logic/qwik-hook-leak": {
48778
+ "db/missing-not-null": {
47442
48779
  recall: 0,
47443
48780
  fpRate: 0,
47444
48781
  ratio: 0,
47445
48782
  precision: 0,
47446
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48783
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47447
48784
  verdict: "DORMANT",
47448
- defaultOff: true,
47449
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Hevery, M. (2022), \u2018Qwik: A Resumable JavaScript Framework\u2019, ACM SIGPLAN OOPSLA companion; Builder.io Technical Report. (Qwik resumability \u2014 serializing state avoids hydration cost.)",
47450
- aiSpecific: true
48785
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48786
+ aiSpecific: false,
48787
+ _v7Verdict: "DORMANT",
48788
+ _v7Lift: 0,
48789
+ _v7Recall: 0,
48790
+ _v7FpRate: 0,
48791
+ _v7Precision: 0,
48792
+ _v8Verdict: "DORMANT",
48793
+ _v8Lift: 1,
48794
+ defaultOff: true
47451
48795
  },
47452
- "test/missing-edge-case": {
48796
+ "db/naming-inconsistency": {
47453
48797
  recall: 0,
47454
48798
  fpRate: 0,
47455
48799
  ratio: 0,
47456
48800
  precision: 0,
47457
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48801
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47458
48802
  verdict: "DORMANT",
47459
- defaultOff: true,
47460
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Myers, G. J. (1979), *The Art of Software Testing*, Wiley-Interscience (canonical boundary value analysis reference); Beizer, B. (1990), *Software Testing Techniques*, 2nd ed., Van Nostrand Reinhold. (Boundary value analysis \u2014 empty / zero / max / null / type-boundary cases.)",
47461
- aiSpecific: true
48803
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
48804
+ aiSpecific: false,
48805
+ _v7Verdict: "DORMANT",
48806
+ _v7Lift: 0,
48807
+ _v7Recall: 0,
48808
+ _v7FpRate: 0,
48809
+ _v7Precision: 0,
48810
+ _v8Verdict: "DORMANT",
48811
+ _v8Lift: 1,
48812
+ defaultOff: true
47462
48813
  },
47463
- "typo/calc-fontsize": {
48814
+ "db/sql-concat": {
48815
+ recall: 5e-4,
48816
+ fpRate: 1e-4,
48817
+ ratio: 7511.26,
48818
+ precision: 0.8343,
48819
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48820
+ verdict: "USEFUL",
48821
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=141, FP=28, P=83.4%, FPR=0.01%, lift=7511.26. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=141, FP=28).",
48822
+ aiSpecific: true,
48823
+ _v7Verdict: "DORMANT",
48824
+ _v7Lift: 1,
48825
+ _v7Recall: 0,
48826
+ _v7FpRate: 0,
48827
+ _v7Precision: 0,
48828
+ _v8Verdict: "USEFUL",
48829
+ _v8Lift: 2046.08
48830
+ },
48831
+ "dead/dead-branch": {
48832
+ recall: 2e-4,
48833
+ fpRate: 4e-4,
48834
+ ratio: 1080.68,
48835
+ precision: 0.4244,
48836
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48837
+ verdict: "OK",
48838
+ _calibrationNote: "v8.5 (v0.18.9): v85 verdict=OK, v7 was DORMANT, v8 was OK. v0.18.8 v8a first measurement (1000 files): see docs/research/v0.18.8-dead-rules-measurement.md.",
48839
+ aiSpecific: true,
48840
+ _v7Verdict: "DORMANT",
48841
+ _v7Lift: 1,
48842
+ _v7Recall: 0,
48843
+ _v7FpRate: 0,
48844
+ _v7Precision: 0,
48845
+ _v8Verdict: "OK",
48846
+ _v8Lift: 294.38
48847
+ },
48848
+ "dead/unreachable": {
48849
+ recall: 1e-4,
48850
+ fpRate: 6e-4,
48851
+ ratio: 153.14,
48852
+ precision: 0.0966,
48853
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48854
+ verdict: "OK",
48855
+ _calibrationNote: "v8.5 (v0.18.9): v85 verdict=OK, v7 was DORMANT, v8 was OK. v0.18.8 v8a first measurement (1000 files): see docs/research/v0.18.8-dead-rules-measurement.md.",
48856
+ aiSpecific: true,
48857
+ _v7Verdict: "DORMANT",
48858
+ _v7Lift: 1,
48859
+ _v7Recall: 0,
48860
+ _v7FpRate: 0,
48861
+ _v7Precision: 0,
48862
+ _v8Verdict: "OK",
48863
+ _v8Lift: 41.71
48864
+ },
48865
+ "dead/unused-import": {
48866
+ recall: 0.0379,
48867
+ fpRate: 0.0222,
48868
+ ratio: 30.06,
48869
+ precision: 0.6662,
48870
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48871
+ verdict: "USEFUL",
48872
+ _calibrationNote: "v8.5 (v0.18.9): v85 verdict=USEFUL, v7 was DORMANT, v8 was USEFUL. v0.18.8 v8a first measurement (1000 files): see docs/research/v0.18.8-dead-rules-measurement.md.",
48873
+ aiSpecific: true,
48874
+ _v7Verdict: "DORMANT",
48875
+ _v7Lift: 1,
48876
+ _v7Recall: 0,
48877
+ _v7FpRate: 0,
48878
+ _v7Precision: 0,
48879
+ _v8Verdict: "USEFUL",
48880
+ _v8Lift: 8.19
48881
+ },
48882
+ "dead/unused-local": {
48883
+ recall: 0.0477,
48884
+ fpRate: 74e-4,
48885
+ ratio: 120.18,
48886
+ precision: 0.8834,
48887
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48888
+ verdict: "USEFUL",
48889
+ _calibrationNote: "v8.5 (v0.18.9): v85 verdict=USEFUL, v7 was DORMANT, v8 was USEFUL. v0.18.8 v8a first measurement (1000 files): see docs/research/v0.18.8-dead-rules-measurement.md. v0.19 default-on (explicit defaultOff: false): P 88.3% / FPR 0.74% / 120x lift \u2014 code hygiene.",
48890
+ aiSpecific: true,
48891
+ _v7Verdict: "DORMANT",
48892
+ _v7Lift: 1,
48893
+ _v7Recall: 0,
48894
+ _v7FpRate: 0,
48895
+ _v7Precision: 0,
48896
+ _v8Verdict: "USEFUL",
48897
+ _v8Lift: 32.74,
48898
+ defaultOff: false
48899
+ },
48900
+ "dead/unused-parameter": {
48901
+ recall: 8e-4,
48902
+ fpRate: 27e-4,
48903
+ ratio: 98,
48904
+ precision: 0.2628,
48905
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48906
+ verdict: "OK",
48907
+ _calibrationNote: "v8.5 (v0.18.9): v85 verdict=OK, v7 was DORMANT, v8 was OK. v0.18.8 v8a first measurement (1000 files): see docs/research/v0.18.8-dead-rules-measurement.md.",
48908
+ aiSpecific: true,
48909
+ _v7Verdict: "DORMANT",
48910
+ _v7Lift: 1,
48911
+ _v7Recall: 0,
48912
+ _v7FpRate: 0,
48913
+ _v7Precision: 0,
48914
+ _v8Verdict: "OK",
48915
+ _v8Lift: 26.7
48916
+ },
48917
+ "docs/broken-link": {
48918
+ recall: 14e-4,
48919
+ fpRate: 23e-4,
48920
+ ratio: 178.13,
48921
+ precision: 0.4155,
48922
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48923
+ verdict: "OK",
48924
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=418, FP=588, P=41.6%, FPR=0.23%, lift=178.13. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was OK (TP=418, FP=588).",
48925
+ aiSpecific: false,
48926
+ _v7Verdict: "DORMANT",
48927
+ _v7Lift: 1,
48928
+ _v7Recall: 0,
48929
+ _v7FpRate: 0,
48930
+ _v7Precision: 0,
48931
+ _v8Verdict: "OK",
48932
+ _v8Lift: 48.52
48933
+ },
48934
+ "docs/expired-code-example": {
47464
48935
  recall: 0,
47465
48936
  fpRate: 0,
47466
48937
  ratio: 0,
47467
48938
  precision: 0,
47468
- lastCalibratedAt: "2026-06-25T00:00:00Z",
48939
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48940
+ verdict: "INVERTED",
48941
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=0, FP=3, P=0.0%, FPR=0.00%, lift=0.00. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was INVERTED (TP=0, FP=3).",
48942
+ aiSpecific: false,
48943
+ _v7Verdict: "DORMANT",
48944
+ _v7Lift: 1,
48945
+ _v7Recall: 0,
48946
+ _v7FpRate: 0,
48947
+ _v7Precision: 0,
48948
+ _v8Verdict: "INVERTED",
48949
+ _v8Lift: 0,
48950
+ defaultOff: true
48951
+ },
48952
+ "docs/stale-function-reference": {
48953
+ recall: 26e-4,
48954
+ fpRate: 5e-4,
48955
+ ratio: 1638.47,
48956
+ precision: 0.8515,
48957
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48958
+ verdict: "USEFUL",
48959
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=751, FP=131, P=85.1%, FPR=0.05%, lift=1638.47. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=751, FP=131).",
48960
+ aiSpecific: false,
48961
+ _v7Verdict: "DORMANT",
48962
+ _v7Lift: 1,
48963
+ _v7Recall: 0,
48964
+ _v7FpRate: 0,
48965
+ _v7Precision: 0,
48966
+ _v8Verdict: "USEFUL",
48967
+ _v8Lift: 446.32
48968
+ },
48969
+ "docs/stale-package-reference": {
48970
+ recall: 2e-4,
48971
+ fpRate: 4e-4,
48972
+ ratio: 939.43,
48973
+ precision: 0.3429,
48974
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48975
+ verdict: "OK",
48976
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=48, FP=92, P=34.3%, FPR=0.04%, lift=939.43. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was OK (TP=48, FP=92).",
48977
+ aiSpecific: false,
48978
+ _v7Verdict: "DORMANT",
48979
+ _v7Lift: 1,
48980
+ _v7Recall: 0,
48981
+ _v7FpRate: 0,
48982
+ _v7Precision: 0,
48983
+ _v8Verdict: "OK",
48984
+ _v8Lift: 255.9
48985
+ },
48986
+ "dup/identical-block": {
48987
+ recall: 0,
48988
+ fpRate: 0,
48989
+ ratio: 1,
48990
+ precision: 0,
48991
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47469
48992
  verdict: "DORMANT",
47470
- defaultOff: true,
47471
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Marcotte, E. (2016), *Responsive Design: Patterns & Principles*, A Book Apart; Brown, J. (2018), *Every Layout*, self-published (rel='https://every-layout.dev'). (Fluid typography via clamp(min, preferred, max).)",
47472
- aiSpecific: false
48993
+ _calibrationNote: "v0.19: new rule, not yet calibrated. v8.5 calibration does not run dup/* rules. Scheduled for v0.20 calibration on near-dup corpus.",
48994
+ aiSpecific: false,
48995
+ _v7Verdict: "DORMANT",
48996
+ _v7Lift: 1,
48997
+ _v7Recall: 0,
48998
+ _v7FpRate: 0,
48999
+ _v7Precision: 0,
49000
+ _v8Verdict: "DORMANT",
49001
+ _v8Lift: 1,
49002
+ defaultOff: true
47473
49003
  },
47474
- "typo/clamp-offscale": {
49004
+ "go/error-wrap-without-context": {
47475
49005
  recall: 0,
47476
49006
  fpRate: 0,
47477
- ratio: 0,
49007
+ ratio: 1,
47478
49008
  precision: 0,
47479
- lastCalibratedAt: "2026-06-25T00:00:00Z",
49009
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47480
49010
  verdict: "DORMANT",
47481
- defaultOff: true,
47482
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: W3C (2023), CSS Values 4 \xA77.2 (Functional Notations: clamp()); Brown, J. (2018), *Every Layout*. (CSS clamp() \u2014 off-scale (negative, >10rem) values are typos.)",
47483
- aiSpecific: false
49011
+ _calibrationNote: "v0.19: new rule (fmt.Errorf wrap with generic message \u2014 needs operation context). Not yet calibrated. Scheduled for v9 calibration.",
49012
+ aiSpecific: true,
49013
+ _v7Verdict: "DORMANT",
49014
+ _v7Lift: 1,
49015
+ _v7Recall: 0,
49016
+ _v7FpRate: 0,
49017
+ _v7Precision: 0,
49018
+ _v8Verdict: "DORMANT",
49019
+ _v8Lift: 1,
49020
+ defaultOff: true
47484
49021
  },
47485
- "typo/math-cta-vocabulary": {
49022
+ "go/nil-slice-vs-empty": {
47486
49023
  recall: 0,
47487
49024
  fpRate: 0,
47488
- ratio: 0,
49025
+ ratio: 1,
47489
49026
  precision: 0,
47490
- lastCalibratedAt: "2026-06-25T00:00:00Z",
49027
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47491
49028
  verdict: "DORMANT",
47492
- defaultOff: true,
47493
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Cialdini, R. B. (1984), *Influence: The Psychology of Persuasion*, Harper Business; Krug, S. (2000), *Don't Make Me Think*, 2nd ed., New Riders. (CTA wording \u2014 vague labels reduce click-through per Cialdini commitment/consistency.)",
47494
- aiSpecific: true
49029
+ _calibrationNote: "v0.19: new rule (Variable declared nil slice but assigned empty slice). Not yet calibrated. Scheduled for v9 calibration.",
49030
+ aiSpecific: true,
49031
+ _v7Verdict: "DORMANT",
49032
+ _v7Lift: 1,
49033
+ _v7Recall: 0,
49034
+ _v7FpRate: 0,
49035
+ _v7Precision: 0,
49036
+ _v8Verdict: "DORMANT",
49037
+ _v8Lift: 1,
49038
+ defaultOff: true
47495
49039
  },
47496
- "layout/forced-layout": {
49040
+ "go/struct-tag-inconsistency": {
47497
49041
  recall: 0,
47498
49042
  fpRate: 0,
47499
- ratio: 0,
49043
+ ratio: 1,
47500
49044
  precision: 0,
47501
- lastCalibratedAt: "2026-06-25T00:00:00Z",
49045
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47502
49046
  verdict: "DORMANT",
47503
- defaultOff: true,
47504
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Mozilla Developer Network (2023), CSS Grid Layout, https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout; W3C (2023), CSS Grid Layout Module Level 3, W3C CR-css-grid-3-20231218. (CSS Grid spec \u2014 forced layout is a fallback hack, not the idiomatic approach.)",
47505
- aiSpecific: true
49047
+ _calibrationNote: "v0.19: new rule (Struct fields mix json tag styles). Not yet calibrated. Scheduled for v9 calibration.",
49048
+ aiSpecific: true,
49049
+ _v7Verdict: "DORMANT",
49050
+ _v7Lift: 1,
49051
+ _v7Recall: 0,
49052
+ _v7FpRate: 0,
49053
+ _v7Precision: 0,
49054
+ _v8Verdict: "DORMANT",
49055
+ _v8Lift: 1,
49056
+ defaultOff: true
47506
49057
  },
47507
- "visual/generic-centering": {
49058
+ "layout/forced-layout": {
47508
49059
  recall: 0,
47509
49060
  fpRate: 0,
47510
49061
  ratio: 0,
47511
49062
  precision: 0,
47512
- lastCalibratedAt: "2026-06-25T00:00:00Z",
49063
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47513
49064
  verdict: "DORMANT",
47514
- defaultOff: true,
47515
- _calibrationNote: "v0.10.1 ship \u2014 not in v4 per-rule table. Default-off until calibration data lands. Backed by: Wertheimer, M. (1923), \u2018Untersuchungen zur Lehre von der Gestalt II\u2019, Psychologische Forschung 4:301-350; M\xFCller-Brockmann, J. (1981), *Grid Systems in Graphic Design*, Niggli. (Gestalt centering + grid systems \u2014 generic centering is anti-grid.)",
47516
- aiSpecific: true
49065
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49066
+ aiSpecific: true,
49067
+ _v7Verdict: "DORMANT",
49068
+ _v7Lift: 0,
49069
+ _v7Recall: 0,
49070
+ _v7FpRate: 0,
49071
+ _v7Precision: 0,
49072
+ _v8Verdict: "DORMANT",
49073
+ _v8Lift: 1,
49074
+ defaultOff: true
47517
49075
  },
47518
- "product/terminology-drift": {
47519
- recall: 85e-4,
47520
- fpRate: 28e-4,
47521
- ratio: 3,
47522
- precision: 0.7956,
47523
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47524
- verdict: "HYGIENE",
47525
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=2028, FP=521, P=79.6%, FPR=0.28%, lift=3.0. aiSpecific=False.",
47526
- aiSpecific: false
47527
- },
47528
- "product/ux-pattern-fragmentation": {
47529
- recall: 1e-4,
47530
- fpRate: 0,
47531
- ratio: 3.09,
47532
- precision: 0.8,
47533
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49076
+ "layout/gap-monopoly": {
49077
+ recall: 3e-4,
49078
+ fpRate: 1e-4,
49079
+ ratio: 8859.33,
49080
+ precision: 0.8083,
49081
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47534
49082
  verdict: "USEFUL",
47535
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=32, FP=8, P=80.0%, FPR=0.00%, lift=3.1. aiSpecific=True.",
47536
- aiSpecific: true
49083
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=97, FP=23, P=80.8%, FPR=0.01%, lift=8859.33. v7 was USEFUL (TP=88, FP=23, lift=6322.11). v8 was USEFUL (TP=9, FP=0).",
49084
+ aiSpecific: false,
49085
+ _v7Verdict: "USEFUL",
49086
+ _v7Lift: 6322.11,
49087
+ _v7Recall: 4e-4,
49088
+ _v7FpRate: 1e-4,
49089
+ _v7Precision: 0.7928,
49090
+ _v8Verdict: "USEFUL",
49091
+ _v8Lift: 99999
47537
49092
  },
47538
- "security/sql-construction": {
47539
- recall: 3e-3,
47540
- fpRate: 18e-4,
47541
- ratio: 1.63,
47542
- precision: 0.6788,
47543
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47544
- verdict: "OK",
47545
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. OK \u2014 TP=710, FP=336, P=67.9%, FPR=0.18%, lift=1.6. aiSpecific=True.",
47546
- aiSpecific: true
49093
+ "layout/math-element-uniformity": {
49094
+ recall: 29e-4,
49095
+ fpRate: 1e-3,
49096
+ ratio: 770.74,
49097
+ precision: 0.7705,
49098
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49099
+ verdict: "USEFUL",
49100
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=846, FP=252, P=77.0%, FPR=0.10%, lift=770.74. v7 was USEFUL (TP=666, FP=225, lift=609.32). v8 was USEFUL (TP=180, FP=27).",
49101
+ aiSpecific: true,
49102
+ _v7Verdict: "USEFUL",
49103
+ _v7Lift: 609.32,
49104
+ _v7Recall: 28e-4,
49105
+ _v7FpRate: 12e-4,
49106
+ _v7Precision: 0.7475,
49107
+ _v8Verdict: "USEFUL",
49108
+ _v8Lift: 2211.5
47547
49109
  },
47548
- "security/missing-auth-check": {
47549
- recall: 63e-4,
47550
- fpRate: 4e-4,
47551
- ratio: 15.3,
47552
- precision: 0.9247,
47553
- lastCalibratedAt: "2026-06-26T22:30:00Z",
49110
+ "layout/math-grid-uniformity": {
49111
+ recall: 3e-4,
49112
+ fpRate: 1e-4,
49113
+ ratio: 5839.98,
49114
+ precision: 0.7182,
49115
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47554
49116
  verdict: "USEFUL",
47555
- _calibrationNote: "v4 corpus (2026-06-25): 95,599 neg + 76,550 pos (frontend, TS/TSX/JS/JSX). USEFUL \u2014 TP=479, FP=39, P=92.5%, FPR=0.04%, lift=15.3.",
47556
- aiSpecific: false
49117
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=79, FP=31, P=71.8%, FPR=0.01%, lift=5839.98. v7 was USEFUL (TP=67, FP=30, lift=4222.91). v8 was USEFUL (TP=12, FP=1).",
49118
+ aiSpecific: true,
49119
+ _v7Verdict: "USEFUL",
49120
+ _v7Lift: 4222.91,
49121
+ _v7Recall: 3e-4,
49122
+ _v7FpRate: 2e-4,
49123
+ _v7Precision: 0.6907,
49124
+ _v8Verdict: "USEFUL",
49125
+ _v8Lift: 63384.92
47557
49126
  },
47558
- "security/dangerous-cors": {
47559
- recall: 5e-4,
47560
- fpRate: 5e-4,
47561
- ratio: 1.05,
47562
- precision: 0.5758,
47563
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47564
- verdict: "NOISY",
47565
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. NOISY \u2014 TP=114, FP=84, P=57.6%, FPR=0.05%, lift=1.0. aiSpecific=True.",
47566
- defaultOff: true,
47567
- aiSpecific: true
49127
+ "layout/spacing-grid": {
49128
+ recall: 2e-4,
49129
+ fpRate: 2e-4,
49130
+ ratio: 4051.29,
49131
+ precision: 0.6429,
49132
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49133
+ verdict: "USEFUL",
49134
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=72, FP=40, P=64.3%, FPR=0.02%, lift=4051.29. v7 was USEFUL (TP=39, FP=15, lift=8831.00). v8 was USEFUL (TP=33, FP=25).",
49135
+ aiSpecific: false,
49136
+ _v7Verdict: "USEFUL",
49137
+ _v7Lift: 8831,
49138
+ _v7Recall: 2e-4,
49139
+ _v7FpRate: 1e-4,
49140
+ _v7Precision: 0.7222,
49141
+ _v8Verdict: "USEFUL",
49142
+ _v8Lift: 1562.77
47568
49143
  },
47569
- "test/duplicate-setup": {
47570
- recall: 1e-4,
49144
+ "logic/bayesian-conditional": {
49145
+ recall: 0,
47571
49146
  fpRate: 0,
47572
- ratio: 3.6,
47573
- precision: 0.8235,
47574
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49147
+ ratio: 0,
49148
+ precision: 0,
49149
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49150
+ verdict: "DORMANT",
49151
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49152
+ aiSpecific: true,
49153
+ _v7Verdict: "DORMANT",
49154
+ _v7Lift: 0,
49155
+ _v7Recall: 0,
49156
+ _v7FpRate: 0,
49157
+ _v7Precision: 0,
49158
+ _v8Verdict: "DORMANT",
49159
+ _v8Lift: 1,
49160
+ defaultOff: true
49161
+ },
49162
+ "logic/boundary-violation": {
49163
+ recall: 0.0274,
49164
+ fpRate: 0.0137,
49165
+ ratio: 51.32,
49166
+ precision: 0.7006,
49167
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47575
49168
  verdict: "USEFUL",
47576
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=14, FP=3, P=82.4%, FPR=0.00%, lift=3.6. aiSpecific=True.",
47577
- aiSpecific: true
49169
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=8051, FP=3441, P=70.1%, FPR=1.37%, lift=51.32. v7 was USEFUL (TP=6282, FP=3235, lift=37.42). v8 was USEFUL (TP=1769, FP=206).",
49170
+ aiSpecific: false,
49171
+ _v7Verdict: "USEFUL",
49172
+ _v7Lift: 37.42,
49173
+ _v7Recall: 0.0265,
49174
+ _v7FpRate: 0.0176,
49175
+ _v7Precision: 0.6601,
49176
+ _v8Verdict: "USEFUL",
49177
+ _v8Lift: 298.57
47578
49178
  },
47579
49179
  "logic/ghost-defensive": {
47580
49180
  recall: 1e-4,
47581
49181
  fpRate: 0,
47582
- ratio: 5.79,
47583
- precision: 0.8824,
47584
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49182
+ ratio: 112035.56,
49183
+ precision: 0.8889,
49184
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47585
49185
  verdict: "USEFUL",
47586
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=15, FP=2, P=88.2%, FPR=0.00%, lift=5.8. aiSpecific=True.",
47587
- aiSpecific: true
49186
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=16, FP=2, P=88.9%, FPR=0.00%, lift=112035.56. v7 was USEFUL (TP=15, FP=2, lift=80917.50). v8 was USEFUL (TP=1, FP=0). v0.19 default-on (explicit defaultOff: false): P 88.9% / 112k lift \u2014 code smell.",
49187
+ aiSpecific: true,
49188
+ _v7Verdict: "USEFUL",
49189
+ _v7Lift: 80917.5,
49190
+ _v7Recall: 1e-4,
49191
+ _v7FpRate: 0,
49192
+ _v7Precision: 0.8824,
49193
+ _v8Verdict: "USEFUL",
49194
+ _v8Lift: 99999,
49195
+ defaultOff: false
47588
49196
  },
47589
- "typo/calc-raw-px": {
47590
- recall: 0,
47591
- fpRate: 0,
47592
- ratio: 2.32,
47593
- precision: 0.75,
47594
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47595
- verdict: "HYGIENE",
47596
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=3, FP=1, P=75.0%, FPR=0.00%, lift=2.3. aiSpecific=False.",
47597
- aiSpecific: false
49197
+ "logic/heaps-deviation": {
49198
+ recall: 0.0126,
49199
+ fpRate: 0.0178,
49200
+ ratio: 25.54,
49201
+ precision: 0.4536,
49202
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49203
+ verdict: "OK",
49204
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=3716, FP=4477, P=45.4%, FPR=1.78%, lift=25.54. v7 was USEFUL (TP=2924, FP=2430, lift=41.22). v8 was OK (TP=792, FP=2047).",
49205
+ aiSpecific: false,
49206
+ _v7Verdict: "USEFUL",
49207
+ _v7Lift: 41.22,
49208
+ _v7Recall: 0.0123,
49209
+ _v7FpRate: 0.0132,
49210
+ _v7Precision: 0.5461,
49211
+ _v8Verdict: "OK",
49212
+ _v8Lift: 9.36
47598
49213
  },
47599
- "security/fail-open-auth": {
47600
- recall: 0,
47601
- fpRate: 0,
47602
- ratio: 99.99,
47603
- precision: 1,
47604
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49214
+ "logic/key-prop-missing": {
49215
+ recall: 16e-4,
49216
+ fpRate: 11e-4,
49217
+ ratio: 574.52,
49218
+ precision: 0.629,
49219
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47605
49220
  verdict: "USEFUL",
47606
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=1, FP=0, P=100.0%, FPR=0.00%, lift=inf. aiSpecific=True.",
47607
- aiSpecific: true
49221
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=468, FP=276, P=62.9%, FPR=0.11%, lift=574.52. v7 was USEFUL (TP=416, FP=250, lift=458.26). v8 was USEFUL (TP=52, FP=26).",
49222
+ aiSpecific: false,
49223
+ _v7Verdict: "USEFUL",
49224
+ _v7Lift: 458.26,
49225
+ _v7Recall: 18e-4,
49226
+ _v7FpRate: 14e-4,
49227
+ _v7Precision: 0.6246,
49228
+ _v8Verdict: "USEFUL",
49229
+ _v8Lift: 1760.69
47608
49230
  },
47609
- "wcag/focus-obscured": {
47610
- recall: 33e-4,
47611
- fpRate: 9e-4,
47612
- ratio: 3.51,
47613
- precision: 0.82,
47614
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47615
- verdict: "HYGIENE",
47616
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=797, FP=175, P=82.0%, FPR=0.09%, lift=3.5. aiSpecific=False.",
47617
- aiSpecific: false
49231
+ "logic/math-any-density": {
49232
+ recall: 17e-4,
49233
+ fpRate: 13e-4,
49234
+ ratio: 464.59,
49235
+ precision: 0.6027,
49236
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49237
+ verdict: "USEFUL",
49238
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=496, FP=327, P=60.3%, FPR=0.13%, lift=464.59. v7 was USEFUL (TP=376, FP=247, lift=448.16). v8 was USEFUL (TP=120, FP=80).",
49239
+ aiSpecific: true,
49240
+ _v7Verdict: "USEFUL",
49241
+ _v7Lift: 448.16,
49242
+ _v7Recall: 16e-4,
49243
+ _v7FpRate: 13e-4,
49244
+ _v7Precision: 0.6035,
49245
+ _v8Verdict: "USEFUL",
49246
+ _v8Lift: 515
47618
49247
  },
47619
- "visual/arbitrary-escape": {
47620
- recall: 25e-4,
47621
- fpRate: 9e-4,
47622
- ratio: 2.74,
47623
- precision: 0.7802,
47624
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49248
+ "logic/math-console-log-storm": {
49249
+ recall: 65e-4,
49250
+ fpRate: 11e-4,
49251
+ ratio: 794.4,
49252
+ precision: 0.8729,
49253
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47625
49254
  verdict: "USEFUL",
47626
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=600, FP=169, P=78.0%, FPR=0.09%, lift=2.7. aiSpecific=True.",
47627
- aiSpecific: true
49255
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1903, FP=277, P=87.3%, FPR=0.11%, lift=794.40. v7 was USEFUL (TP=1678, FP=193, lift=852.30). v8 was USEFUL (TP=225, FP=84).",
49256
+ aiSpecific: true,
49257
+ _v7Verdict: "USEFUL",
49258
+ _v7Lift: 852.3,
49259
+ _v7Recall: 71e-4,
49260
+ _v7FpRate: 11e-4,
49261
+ _v7Precision: 0.8968,
49262
+ _v8Verdict: "USEFUL",
49263
+ _v8Lift: 595.24
47628
49264
  },
47629
- "security/hardcoded-secret": {
49265
+ "logic/math-gini-class-usage": {
47630
49266
  recall: 13e-4,
47631
- fpRate: 7e-4,
47632
- ratio: 1.72,
47633
- precision: 0.6899,
47634
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47635
- verdict: "OK",
47636
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. OK \u2014 TP=307, FP=138, P=69.0%, FPR=0.07%, lift=1.7. aiSpecific=True.",
47637
- aiSpecific: true
47638
- },
47639
- "visual/radius-scale-violation": {
47640
- recall: 4e-4,
47641
- fpRate: 0,
47642
- ratio: 24.7,
47643
- precision: 0.9697,
47644
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47645
- verdict: "HYGIENE",
47646
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=96, FP=3, P=97.0%, FPR=0.00%, lift=24.7. aiSpecific=False.",
47647
- aiSpecific: false
47648
- },
47649
- "test/fake-placeholder": {
47650
- recall: 53e-4,
47651
- fpRate: 19e-4,
47652
- ratio: 2.82,
47653
- precision: 0.7854,
47654
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49267
+ fpRate: 2e-4,
49268
+ ratio: 4648.6,
49269
+ precision: 0.8852,
49270
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47655
49271
  verdict: "USEFUL",
47656
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=1259, FP=344, P=78.5%, FPR=0.19%, lift=2.8. aiSpecific=True.",
47657
- aiSpecific: true
47658
- },
47659
- "security/exposed-env-var": {
47660
- recall: 6e-4,
47661
- fpRate: 7e-4,
47662
- ratio: 0.9,
47663
- precision: 0.5387,
47664
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47665
- verdict: "HYGIENE",
47666
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=146, FP=125, P=53.9%, FPR=0.07%, lift=0.9. aiSpecific=False.",
47667
- aiSpecific: false
47668
- },
47669
- "perf/cls-image": {
47670
- recall: 2e-4,
47671
- fpRate: 3e-4,
47672
- ratio: 0.8,
47673
- precision: 0.5104,
47674
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47675
- verdict: "HYGIENE",
47676
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=49, FP=47, P=51.0%, FPR=0.03%, lift=0.8. aiSpecific=False.",
47677
- aiSpecific: false
47678
- },
47679
- "layout/gap-monopoly": {
47680
- recall: 4e-4,
47681
- fpRate: 1e-4,
47682
- ratio: 2.95,
47683
- precision: 0.7928,
47684
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47685
- verdict: "HYGIENE",
47686
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=88, FP=23, P=79.3%, FPR=0.01%, lift=3.0. aiSpecific=False.",
47687
- aiSpecific: false
47688
- },
47689
- "visual/spacing-scale-violation": {
47690
- recall: 83e-4,
47691
- fpRate: 39e-4,
47692
- ratio: 2.13,
47693
- precision: 0.7342,
47694
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47695
- verdict: "HYGIENE",
47696
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=1981, FP=717, P=73.4%, FPR=0.39%, lift=2.1. aiSpecific=False.",
47697
- aiSpecific: false
47698
- },
47699
- "visual/inline-style-dominance": {
47700
- recall: 96e-4,
47701
- fpRate: 66e-4,
47702
- ratio: 1.46,
47703
- precision: 0.6535,
47704
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47705
- verdict: "HYGIENE",
47706
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=2303, FP=1221, P=65.4%, FPR=0.66%, lift=1.5. aiSpecific=False.",
47707
- aiSpecific: false
49272
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=370, FP=48, P=88.5%, FPR=0.02%, lift=4648.60. v7 was USEFUL (TP=310, FP=47, lift=3388.64). v8 was USEFUL (TP=60, FP=1).",
49273
+ aiSpecific: true,
49274
+ _v7Verdict: "USEFUL",
49275
+ _v7Lift: 3388.64,
49276
+ _v7Recall: 13e-4,
49277
+ _v7FpRate: 3e-4,
49278
+ _v7Precision: 0.8683,
49279
+ _v8Verdict: "USEFUL",
49280
+ _v8Lift: 67541.31
47708
49281
  },
47709
- "layout/spacing-grid": {
49282
+ "logic/math-variable-name-entropy": {
47710
49283
  recall: 2e-4,
47711
49284
  fpRate: 1e-4,
47712
- ratio: 2.01,
47713
- precision: 0.7222,
47714
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47715
- verdict: "HYGIENE",
47716
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=39, FP=15, P=72.2%, FPR=0.01%, lift=2.0. aiSpecific=False.",
47717
- aiSpecific: false
47718
- },
47719
- "wcag/dragging-movements": {
47720
- recall: 0,
47721
- fpRate: 0,
47722
- ratio: 0.51,
47723
- precision: 0.4,
47724
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47725
- verdict: "HYGIENE",
47726
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=2, FP=3, P=40.0%, FPR=0.00%, lift=0.5. aiSpecific=False.",
47727
- aiSpecific: false
49285
+ ratio: 4548.81,
49286
+ precision: 0.6316,
49287
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49288
+ verdict: "USEFUL",
49289
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=60, FP=35, P=63.2%, FPR=0.01%, lift=4548.81. v7 was USEFUL (TP=36, FP=26, lift=4096.07). v8 was USEFUL (TP=24, FP=9).",
49290
+ aiSpecific: false,
49291
+ _v7Verdict: "USEFUL",
49292
+ _v7Lift: 4096.07,
49293
+ _v7Recall: 2e-4,
49294
+ _v7FpRate: 1e-4,
49295
+ _v7Precision: 0.5806,
49296
+ _v8Verdict: "USEFUL",
49297
+ _v8Lift: 5548.85
47728
49298
  },
47729
- "test/weak-assertion": {
47730
- recall: 0.0414,
47731
- fpRate: 86e-4,
47732
- ratio: 4.83,
47733
- precision: 0.8622,
47734
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49299
+ "logic/optimistic-no-rollback": {
49300
+ recall: 12e-4,
49301
+ fpRate: 2e-4,
49302
+ ratio: 3523.57,
49303
+ precision: 0.8527,
49304
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47735
49305
  verdict: "USEFUL",
47736
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=9906, FP=1583, P=86.2%, FPR=0.86%, lift=4.8. aiSpecific=True.",
47737
- aiSpecific: true
49306
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=353, FP=61, P=85.3%, FPR=0.02%, lift=3523.57. v7 was USEFUL (TP=281, FP=60, lift=2519.02). v8 was USEFUL (TP=72, FP=1).",
49307
+ aiSpecific: true,
49308
+ _v7Verdict: "USEFUL",
49309
+ _v7Lift: 2519.02,
49310
+ _v7Recall: 12e-4,
49311
+ _v7FpRate: 3e-4,
49312
+ _v7Precision: 0.824,
49313
+ _v8Verdict: "USEFUL",
49314
+ _v8Lift: 67726.36
47738
49315
  },
47739
- "logic/bayesian-conditional": {
49316
+ "logic/qwik-hook-leak": {
47740
49317
  recall: 0,
47741
49318
  fpRate: 0,
47742
49319
  ratio: 0,
47743
49320
  precision: 0,
47744
- lastCalibratedAt: "2026-06-27T00:00:00Z",
49321
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47745
49322
  verdict: "DORMANT",
47746
- defaultOff: true,
47747
- _calibrationNote: "v0.12.0 ship \u2014 new Bayesian LR-combiner rule (Bento et al. 2024 *Neurocomputing*). Default-off until v0.12 corpus re-calibration lands. Threshold P(AI|fires) \u2265 0.7; posterior computed from per-rule likelihood ratios.",
47748
- aiSpecific: true
49323
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49324
+ aiSpecific: true,
49325
+ _v7Verdict: "DORMANT",
49326
+ _v7Lift: 0,
49327
+ _v7Recall: 0,
49328
+ _v7FpRate: 0,
49329
+ _v7Precision: 0,
49330
+ _v8Verdict: "DORMANT",
49331
+ _v8Lift: 1,
49332
+ defaultOff: true
47749
49333
  },
47750
- "logic/heaps-deviation": {
47751
- recall: 0.0122,
47752
- fpRate: 0.0132,
47753
- ratio: 0.93,
47754
- precision: 0.5461,
47755
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47756
- verdict: "HYGIENE",
47757
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=2924, FP=2430, P=54.6%, FPR=1.32%, lift=0.9. aiSpecific=False.",
47758
- aiSpecific: false
47759
- },
47760
- "logic/ks-distribution-shift": {
47761
- recall: 0.6375,
47762
- fpRate: 0.338,
47763
- ratio: 1.89,
47764
- precision: 0.7096,
47765
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47766
- verdict: "HYGIENE",
47767
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=152391, FP=62358, P=71.0%, FPR=33.80%, lift=1.9. aiSpecific=False.",
47768
- aiSpecific: false
49334
+ "logic/reactive-hook-soup": {
49335
+ recall: 41e-4,
49336
+ fpRate: 6e-4,
49337
+ ratio: 1370.15,
49338
+ precision: 0.8805,
49339
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49340
+ verdict: "USEFUL",
49341
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1194, FP=162, P=88.1%, FPR=0.06%, lift=1370.15. v7 was USEFUL (TP=901, FP=162, lift=959.64). v8 was USEFUL (TP=293, FP=0).",
49342
+ aiSpecific: true,
49343
+ _v7Verdict: "USEFUL",
49344
+ _v7Lift: 959.64,
49345
+ _v7Recall: 38e-4,
49346
+ _v7FpRate: 9e-4,
49347
+ _v7Precision: 0.8476,
49348
+ _v8Verdict: "USEFUL",
49349
+ _v8Lift: 99999
47769
49350
  },
47770
49351
  "logic/zipf-slope-anomaly": {
47771
- recall: 0.0156,
47772
- fpRate: 87e-4,
47773
- ratio: 1.79,
47774
- precision: 0.6983,
47775
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47776
- verdict: "HYGIENE",
47777
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. HYGIENE \u2014 TP=3718, FP=1606, P=69.8%, FPR=0.87%, lift=1.8. aiSpecific=False.",
47778
- aiSpecific: false
49352
+ recall: 0.0168,
49353
+ fpRate: 0.0111,
49354
+ ratio: 57.13,
49355
+ precision: 0.6369,
49356
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49357
+ verdict: "USEFUL",
49358
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=4928, FP=2810, P=63.7%, FPR=1.11%, lift=57.13. v7 was USEFUL (TP=3718, FP=1606, lift=79.75). v8 was USEFUL (TP=1210, FP=1204).",
49359
+ aiSpecific: false,
49360
+ _v7Verdict: "USEFUL",
49361
+ _v7Lift: 79.75,
49362
+ _v7Recall: 0.0157,
49363
+ _v7FpRate: 88e-4,
49364
+ _v7Precision: 0.6983,
49365
+ _v8Verdict: "USEFUL",
49366
+ _v8Lift: 28.59
47779
49367
  },
47780
- "ai/markdown-leakage": {
47781
- recall: 0,
49368
+ "logic/zombie-state": {
49369
+ recall: 1e-4,
47782
49370
  fpRate: 0,
47783
- ratio: 1.93,
47784
- precision: 0.7143,
47785
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47786
- verdict: "OK",
47787
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. OK \u2014 TP=5, FP=2, P=71.4%, FPR=0.00%, lift=1.9. aiSpecific=True.",
47788
- aiSpecific: true
49371
+ ratio: 119891.71,
49372
+ precision: 0.9512,
49373
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49374
+ verdict: "USEFUL",
49375
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=39, FP=2, P=95.1%, FPR=0.00%, lift=119891.71. v7 was USEFUL (TP=24, FP=2, lift=84652.15). v8 was USEFUL (TP=15, FP=0).",
49376
+ aiSpecific: true,
49377
+ _v7Verdict: "USEFUL",
49378
+ _v7Lift: 84652.15,
49379
+ _v7Recall: 1e-4,
49380
+ _v7FpRate: 0,
49381
+ _v7Precision: 0.9231,
49382
+ _v8Verdict: "USEFUL",
49383
+ _v8Lift: 99999
47789
49384
  },
47790
- "ai/comment-ratio": {
47791
- recall: 0.2523,
47792
- fpRate: 0.1619,
47793
- ratio: 1.56,
47794
- precision: 0.6687,
47795
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47796
- verdict: "OK",
47797
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. OK \u2014 TP=60308, FP=29876, P=66.9%, FPR=16.19%, lift=1.6. aiSpecific=True.",
47798
- aiSpecific: true
49385
+ "perf/cls-image": {
49386
+ recall: 2e-4,
49387
+ fpRate: 2e-4,
49388
+ ratio: 2895.78,
49389
+ precision: 0.5514,
49390
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49391
+ verdict: "USEFUL",
49392
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=59, FP=48, P=55.1%, FPR=0.02%, lift=2895.78. v7 was USEFUL (TP=49, FP=47, lift=1991.85). v8 was USEFUL (TP=10, FP=1).",
49393
+ aiSpecific: false,
49394
+ _v7Verdict: "USEFUL",
49395
+ _v7Lift: 1991.85,
49396
+ _v7Recall: 2e-4,
49397
+ _v7FpRate: 3e-4,
49398
+ _v7Precision: 0.5104,
49399
+ _v8Verdict: "USEFUL",
49400
+ _v8Lift: 62424.55
47799
49401
  },
47800
- "ai/whitespace-regularity": {
47801
- recall: 0.0731,
47802
- fpRate: 0.0728,
47803
- ratio: 1,
47804
- precision: 0.5652,
47805
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47806
- verdict: "NOISY",
47807
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. NOISY \u2014 TP=17466, FP=13438, P=56.5%, FPR=7.28%, lift=1.0. aiSpecific=True.",
47808
- defaultOff: true,
47809
- aiSpecific: true
49402
+ "perf/css-bloat": {
49403
+ recall: 0.0126,
49404
+ fpRate: 24e-4,
49405
+ ratio: 365.63,
49406
+ precision: 0.8616,
49407
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49408
+ verdict: "USEFUL",
49409
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=3697, FP=594, P=86.2%, FPR=0.24%, lift=365.63. v7 was USEFUL (TP=2790, FP=591, lift=256.10). v8 was USEFUL (TP=907, FP=3).",
49410
+ aiSpecific: false,
49411
+ _v7Verdict: "USEFUL",
49412
+ _v7Lift: 256.1,
49413
+ _v7Recall: 0.0118,
49414
+ _v7FpRate: 32e-4,
49415
+ _v7Precision: 0.8252,
49416
+ _v8Verdict: "USEFUL",
49417
+ _v8Lift: 22813.54
47810
49418
  },
47811
- "ai/text-like-ratio": {
49419
+ "perf/halstead-anomaly": {
47812
49420
  recall: 0,
47813
49421
  fpRate: 0,
47814
- ratio: 2.32,
47815
- precision: 0.75,
47816
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49422
+ ratio: 75624,
49423
+ precision: 0.6,
49424
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47817
49425
  verdict: "USEFUL",
47818
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=3, FP=1, P=75.0%, FPR=0.00%, lift=2.3. aiSpecific=True.",
47819
- aiSpecific: true
49426
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=3, FP=2, P=60.0%, FPR=0.00%, lift=75624.00. v7 was USEFUL (TP=3, FP=1, lift=137559.75). v8 was INVERTED (TP=0, FP=1).",
49427
+ aiSpecific: true,
49428
+ _v7Verdict: "USEFUL",
49429
+ _v7Lift: 137559.75,
49430
+ _v7Recall: 0,
49431
+ _v7FpRate: 0,
49432
+ _v7Precision: 0.75,
49433
+ _v8Verdict: "INVERTED",
49434
+ _v8Lift: 0
47820
49435
  },
47821
- "ai/errors-near-eof": {
47822
- recall: 0.0697,
47823
- fpRate: 0.0548,
47824
- ratio: 1.27,
47825
- precision: 0.6225,
47826
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47827
- verdict: "NOISY",
47828
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. NOISY \u2014 TP=16673, FP=10112, P=62.2%, FPR=5.48%, lift=1.3. aiSpecific=True.",
47829
- defaultOff: true,
47830
- aiSpecific: true
49436
+ "product/terminology-drift": {
49437
+ recall: 91e-4,
49438
+ fpRate: 21e-4,
49439
+ ratio: 397.75,
49440
+ precision: 0.8347,
49441
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49442
+ verdict: "USEFUL",
49443
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=2671, FP=529, P=83.5%, FPR=0.21%, lift=397.75. v7 was USEFUL (TP=2028, FP=521, lift=280.09). v8 was USEFUL (TP=643, FP=8).",
49444
+ aiSpecific: false,
49445
+ _v7Verdict: "USEFUL",
49446
+ _v7Lift: 280.09,
49447
+ _v7Recall: 86e-4,
49448
+ _v7FpRate: 28e-4,
49449
+ _v7Precision: 0.7956,
49450
+ _v8Verdict: "USEFUL",
49451
+ _v8Lift: 8477.9
47831
49452
  },
47832
- "ai/any-density": {
47833
- recall: 55e-4,
47834
- fpRate: 41e-4,
47835
- ratio: 1.34,
47836
- precision: 0.634,
47837
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47838
- verdict: "NOISY",
47839
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. NOISY \u2014 TP=1313, FP=758, P=63.4%, FPR=0.41%, lift=1.3. aiSpecific=True.",
47840
- defaultOff: true,
47841
- aiSpecific: true
49453
+ "product/ux-pattern-fragmentation": {
49454
+ recall: 1e-4,
49455
+ fpRate: 0,
49456
+ ratio: 22864.4,
49457
+ precision: 0.8163,
49458
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49459
+ verdict: "USEFUL",
49460
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=40, FP=9, P=81.6%, FPR=0.00%, lift=22864.40. v7 was USEFUL (TP=32, FP=8, lift=18341.30). v8 was USEFUL (TP=8, FP=1).",
49461
+ aiSpecific: true,
49462
+ _v7Verdict: "USEFUL",
49463
+ _v7Lift: 18341.3,
49464
+ _v7Recall: 1e-4,
49465
+ _v7FpRate: 0,
49466
+ _v7Precision: 0.8,
49467
+ _v8Verdict: "USEFUL",
49468
+ _v8Lift: 61037.33
47842
49469
  },
47843
- "ai/renyi-profile": {
47844
- recall: 0,
49470
+ "rust/stringly-typed": {
49471
+ recall: 2e-4,
47845
49472
  fpRate: 0,
47846
- ratio: 0.26,
47847
- precision: 0.25,
47848
- lastCalibratedAt: "2026-06-27T12:00:00Z",
47849
- verdict: "INVERTED",
47850
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. INVERTED \u2014 TP=3, FP=9, P=25.0%, FPR=0.00%, lift=0.3. aiSpecific=True.",
47851
- defaultOff: true,
47852
- aiSpecific: true
49473
+ ratio: 24735.12,
49474
+ precision: 0.8831,
49475
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49476
+ verdict: "USEFUL",
49477
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=68, FP=9, P=88.3%, FPR=0.00%, lift=24735.12. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=68, FP=9).",
49478
+ aiSpecific: true,
49479
+ _v7Verdict: "DORMANT",
49480
+ _v7Lift: 1,
49481
+ _v7Recall: 0,
49482
+ _v7FpRate: 0,
49483
+ _v7Precision: 0,
49484
+ _v8Verdict: "USEFUL",
49485
+ _v8Lift: 6737.89
47853
49486
  },
47854
- "ai/log-rank-histogram": {
49487
+ "rust/todo-macro": {
49488
+ recall: 1e-4,
49489
+ fpRate: 7e-4,
49490
+ ratio: 210.08,
49491
+ precision: 0.1392,
49492
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49493
+ verdict: "OK",
49494
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=27, FP=167, P=13.9%, FPR=0.07%, lift=210.08. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was OK (TP=27, FP=167).",
49495
+ aiSpecific: true,
49496
+ _v7Verdict: "DORMANT",
49497
+ _v7Lift: 1,
49498
+ _v7Recall: 0,
49499
+ _v7FpRate: 0,
49500
+ _v7Precision: 0,
49501
+ _v8Verdict: "OK",
49502
+ _v8Lift: 57.23
49503
+ },
49504
+ "rust/unused-pub-fn": {
47855
49505
  recall: 0,
47856
49506
  fpRate: 0,
47857
- ratio: 0,
47858
- precision: 0,
47859
- lastCalibratedAt: "2026-06-27T00:00:00Z",
49507
+ ratio: 10803.43,
49508
+ precision: 0.3,
49509
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47860
49510
  verdict: "OK",
47861
- _calibrationNote: "v0.13.0 stub: rule implemented per Gehrmann 2019 GLTR; calibration on v7 corpus pending. v0.13.0: marked OK (peer-reviewed math backing; lift=0 because v7 calibration has not been run yet \u2014 will be re-evaluated).",
47862
- aiSpecific: true
49511
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=3, FP=7, P=30.0%, FPR=0.00%, lift=10803.43. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was OK (TP=3, FP=7).",
49512
+ aiSpecific: true,
49513
+ _v7Verdict: "DORMANT",
49514
+ _v7Lift: 1,
49515
+ _v7Recall: 0,
49516
+ _v7FpRate: 0,
49517
+ _v7Precision: 0,
49518
+ _v8Verdict: "OK",
49519
+ _v8Lift: 2942.87
47863
49520
  },
47864
- "ai/segment-surprisal-cv": {
47865
- recall: 0.182,
47866
- fpRate: 0.0812,
47867
- ratio: 2.24,
47868
- precision: 0.7438,
47869
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49521
+ "rust/unwrap-in-production": {
49522
+ recall: 8e-3,
49523
+ fpRate: 77e-4,
49524
+ ratio: 70.81,
49525
+ precision: 0.5475,
49526
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47870
49527
  verdict: "USEFUL",
47871
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=43498, FP=14983, P=74.4%, FPR=8.12%, lift=2.2. aiSpecific=True.",
47872
- aiSpecific: true
49528
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=2358, FP=1949, P=54.7%, FPR=0.77%, lift=70.81. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=2358, FP=1949).",
49529
+ aiSpecific: true,
49530
+ _v7Verdict: "DORMANT",
49531
+ _v7Lift: 1,
49532
+ _v7Recall: 0,
49533
+ _v7FpRate: 0,
49534
+ _v7Precision: 0,
49535
+ _v8Verdict: "USEFUL",
49536
+ _v8Lift: 19.29
47873
49537
  },
47874
- "ai/compression-profile": {
47875
- recall: 0.3139,
47876
- fpRate: 0.1489,
47877
- ratio: 2.11,
47878
- precision: 0.732,
47879
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49538
+ "security/dangerous-cors": {
49539
+ recall: 5e-4,
49540
+ fpRate: 4e-4,
49541
+ ratio: 1721.87,
49542
+ precision: 0.6079,
49543
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47880
49544
  verdict: "USEFUL",
47881
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=75031, FP=27473, P=73.2%, FPR=14.89%, lift=2.1. aiSpecific=True.",
47882
- aiSpecific: true
49545
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=138, FP=89, P=60.8%, FPR=0.04%, lift=1721.87. v7 was USEFUL (TP=114, FP=84, lift=1257.16). v8 was USEFUL (TP=24, FP=5).",
49546
+ aiSpecific: true,
49547
+ _v7Verdict: "USEFUL",
49548
+ _v7Lift: 1257.16,
49549
+ _v7Recall: 5e-4,
49550
+ _v7FpRate: 5e-4,
49551
+ _v7Precision: 0.5758,
49552
+ _v8Verdict: "USEFUL",
49553
+ _v8Lift: 11365.57
47883
49554
  },
47884
- "ai/tailwind-color-overuse": {
47885
- recall: 0.0216,
47886
- fpRate: 52e-4,
47887
- ratio: 4.16,
47888
- precision: 0.8436,
47889
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49555
+ "security/eval": {
49556
+ recall: 1e-4,
49557
+ fpRate: 2e-4,
49558
+ ratio: 1929.18,
49559
+ precision: 0.4286,
49560
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49561
+ verdict: "OK",
49562
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=42, FP=56, P=42.9%, FPR=0.02%, lift=1929.18. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was OK (TP=42, FP=56).",
49563
+ aiSpecific: false,
49564
+ _v7Verdict: "DORMANT",
49565
+ _v7Lift: 1,
49566
+ _v7Recall: 0,
49567
+ _v7FpRate: 0,
49568
+ _v7Precision: 0,
49569
+ _v8Verdict: "OK",
49570
+ _v8Lift: 525.51
49571
+ },
49572
+ "security/exposed-env-var": {
49573
+ recall: 6e-4,
49574
+ fpRate: 5e-4,
49575
+ ratio: 1055.81,
49576
+ precision: 0.5696,
49577
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47890
49578
  verdict: "USEFUL",
47891
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=5169, FP=958, P=84.4%, FPR=0.52%, lift=4.2. aiSpecific=True.",
47892
- aiSpecific: true
49579
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=180, FP=136, P=57.0%, FPR=0.05%, lift=1055.81. v7 was USEFUL (TP=146, FP=125, lift=790.50). v8 was USEFUL (TP=34, FP=11).",
49580
+ aiSpecific: false,
49581
+ _v7Verdict: "USEFUL",
49582
+ _v7Lift: 790.5,
49583
+ _v7Recall: 6e-4,
49584
+ _v7FpRate: 7e-4,
49585
+ _v7Precision: 0.5387,
49586
+ _v8Verdict: "USEFUL",
49587
+ _v8Lift: 4716.52
47893
49588
  },
47894
- "ai/default-react-stack": {
47895
- recall: 1e-3,
49589
+ "security/fail-open-auth": {
49590
+ recall: 0,
47896
49591
  fpRate: 0,
47897
- ratio: 99.99,
47898
- precision: 0.9957,
47899
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49592
+ ratio: 99999,
49593
+ precision: 1,
49594
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47900
49595
  verdict: "USEFUL",
47901
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=231, FP=1, P=99.6%, FPR=0.00%, lift=178.3. aiSpecific=True.",
47902
- aiSpecific: true
49596
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1, FP=0, P=100.0%, FPR=0.00%, lift=inf. v7 was USEFUL (TP=1, FP=0, lift=inf). v8 was DORMANT (TP=0, FP=0). v0.19 default-on (explicit defaultOff: false): P 100% / inf lift \u2014 security must always be on.",
49597
+ aiSpecific: true,
49598
+ _v7Verdict: "USEFUL",
49599
+ _v7Lift: 99999,
49600
+ _v7Recall: 0,
49601
+ _v7FpRate: 0,
49602
+ _v7Precision: 1,
49603
+ _v8Verdict: "DORMANT",
49604
+ _v8Lift: 1,
49605
+ defaultOff: false
47903
49606
  },
47904
- "ai/library-reinvention": {
47905
- recall: 3e-4,
49607
+ "security/hardcoded-secret": {
49608
+ recall: 14e-4,
49609
+ fpRate: 6e-4,
49610
+ ratio: 1269.08,
49611
+ precision: 0.735,
49612
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49613
+ verdict: "USEFUL",
49614
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=405, FP=146, P=73.5%, FPR=0.06%, lift=1269.08. v7 was USEFUL (TP=307, FP=138, lift=916.92). v8 was USEFUL (TP=98, FP=8).",
49615
+ aiSpecific: true,
49616
+ _v7Verdict: "USEFUL",
49617
+ _v7Lift: 916.92,
49618
+ _v7Recall: 13e-4,
49619
+ _v7FpRate: 8e-4,
49620
+ _v7Precision: 0.6899,
49621
+ _v8Verdict: "USEFUL",
49622
+ _v8Lift: 7935.57
49623
+ },
49624
+ "security/localstorage-token": {
49625
+ recall: 1e-4,
47906
49626
  fpRate: 0,
47907
- ratio: 9.88,
47908
- precision: 0.9275,
47909
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49627
+ ratio: 99999,
49628
+ precision: 1,
49629
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47910
49630
  verdict: "USEFUL",
47911
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=64, FP=5, P=92.8%, FPR=0.00%, lift=9.9. aiSpecific=True.",
47912
- aiSpecific: true
49631
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=17, FP=0, P=100.0%, FPR=0.00%, lift=inf. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=17, FP=0).",
49632
+ aiSpecific: false,
49633
+ _v7Verdict: "DORMANT",
49634
+ _v7Lift: 1,
49635
+ _v7Recall: 0,
49636
+ _v7FpRate: 0,
49637
+ _v7Precision: 0,
49638
+ _v8Verdict: "USEFUL",
49639
+ _v8Lift: 99999
47913
49640
  },
47914
- "ai/state-default-overuse": {
47915
- recall: 29e-4,
47916
- fpRate: 9e-4,
47917
- ratio: 3.42,
47918
- precision: 0.8161,
47919
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49641
+ "security/missing-auth-check": {
49642
+ recall: 5e-4,
49643
+ fpRate: 0,
49644
+ ratio: 99999,
49645
+ precision: 1,
49646
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47920
49647
  verdict: "USEFUL",
47921
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=701, FP=158, P=81.6%, FPR=0.09%, lift=3.4. aiSpecific=True.",
47922
- aiSpecific: true
49648
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=157, FP=0, P=100.0%, FPR=0.00%, lift=inf. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=157, FP=0).",
49649
+ aiSpecific: false,
49650
+ _v7Verdict: "DORMANT",
49651
+ _v7Lift: 1,
49652
+ _v7Recall: 0,
49653
+ _v7FpRate: 0,
49654
+ _v7Precision: 0,
49655
+ _v8Verdict: "USEFUL",
49656
+ _v8Lift: 99999
47923
49657
  },
47924
- "ai/fetch-default-overuse": {
47925
- recall: 28e-4,
47926
- fpRate: 4e-4,
47927
- ratio: 6.76,
47928
- precision: 0.8976,
47929
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49658
+ "security/public-admin-route": {
49659
+ recall: 38e-4,
49660
+ fpRate: 38e-4,
49661
+ ratio: 142.2,
49662
+ precision: 0.5376,
49663
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47930
49664
  verdict: "USEFUL",
47931
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=666, FP=76, P=89.8%, FPR=0.04%, lift=6.8. aiSpecific=True.",
47932
- aiSpecific: true
49665
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1108, FP=953, P=53.8%, FPR=0.38%, lift=142.20. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=1108, FP=953).",
49666
+ aiSpecific: false,
49667
+ _v7Verdict: "DORMANT",
49668
+ _v7Lift: 1,
49669
+ _v7Recall: 0,
49670
+ _v7FpRate: 0,
49671
+ _v7Precision: 0,
49672
+ _v8Verdict: "USEFUL",
49673
+ _v8Lift: 38.74
47933
49674
  },
47934
- "ai/console-debug-storm": {
47935
- recall: 8e-3,
47936
- fpRate: 9e-4,
47937
- ratio: 8.43,
47938
- precision: 0.9161,
47939
- lastCalibratedAt: "2026-06-27T12:00:00Z",
49675
+ "security/sql-construction": {
49676
+ recall: 32e-4,
49677
+ fpRate: 15e-4,
49678
+ ratio: 485.42,
49679
+ precision: 0.7183,
49680
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47940
49681
  verdict: "USEFUL",
47941
- _calibrationNote: "v7 corpus re-calibration (2026-06-27, min-date=2025-01-01): 184488 neg + 239054 pos. USEFUL \u2014 TP=1912, FP=175, P=91.6%, FPR=0.09%, lift=8.4. aiSpecific=True.",
47942
- aiSpecific: true
49682
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=951, FP=373, P=71.8%, FPR=0.15%, lift=485.42. v7 was USEFUL (TP=710, FP=336, lift=370.52). v8 was USEFUL (TP=241, FP=37).",
49683
+ aiSpecific: true,
49684
+ _v7Verdict: "USEFUL",
49685
+ _v7Lift: 370.52,
49686
+ _v7Recall: 3e-3,
49687
+ _v7FpRate: 18e-4,
49688
+ _v7Precision: 0.6788,
49689
+ _v8Verdict: "USEFUL",
49690
+ _v8Lift: 1608.86
47943
49691
  },
47944
- "db/missing-fk-index": {
47945
- recall: 0,
49692
+ "security/target-blank-no-noopener": {
49693
+ recall: 4e-4,
47946
49694
  fpRate: 0,
47947
- ratio: 0,
47948
- precision: 0,
47949
- lastCalibratedAt: "2026-06-30T00:00:00Z",
47950
- verdict: "DORMANT",
47951
- defaultOff: true,
47952
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: PostgreSQL Global Development Group (2024), *PostgreSQL 16 Documentation \xA75.4 (Constraints)*, https://www.postgresql.org/docs/16/ddl-constraints.html; Squawk (2023), *Postgres linter rules*, https://github.com/sqllabs/squawk, rule `require-index-for-fk`. (Foreign key columns need a matching index \u2014 sequential scan on parent delete is a canonical Postgres anti-pattern.)",
47953
- aiSpecific: false
49695
+ ratio: 26141.63,
49696
+ precision: 0.9333,
49697
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49698
+ verdict: "USEFUL",
49699
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=126, FP=9, P=93.3%, FPR=0.00%, lift=26141.63. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=126, FP=9).",
49700
+ aiSpecific: false,
49701
+ _v7Verdict: "DORMANT",
49702
+ _v7Lift: 1,
49703
+ _v7Recall: 0,
49704
+ _v7FpRate: 0,
49705
+ _v7Precision: 0,
49706
+ _v8Verdict: "USEFUL",
49707
+ _v8Lift: 7121.02
47954
49708
  },
47955
- "db/duplicate-index": {
47956
- recall: 0,
49709
+ "security/unsafe-html-render": {
49710
+ recall: 13e-4,
49711
+ fpRate: 11e-4,
49712
+ ratio: 513.12,
49713
+ precision: 0.574,
49714
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49715
+ verdict: "USEFUL",
49716
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=380, FP=282, P=57.4%, FPR=0.11%, lift=513.12. v7 was USEFUL (TP=312, FP=160, lift=757.74). v8 was OK (TP=68, FP=122).",
49717
+ aiSpecific: false,
49718
+ _v7Verdict: "USEFUL",
49719
+ _v7Lift: 757.74,
49720
+ _v7Recall: 13e-4,
49721
+ _v7FpRate: 9e-4,
49722
+ _v7Precision: 0.661,
49723
+ _v8Verdict: "OK",
49724
+ _v8Lift: 201.44
49725
+ },
49726
+ "test/duplicate-setup": {
49727
+ recall: 1e-4,
47957
49728
  fpRate: 0,
47958
- ratio: 0,
47959
- precision: 0,
47960
- lastCalibratedAt: "2026-06-30T00:00:00Z",
47961
- verdict: "DORMANT",
47962
- defaultOff: true,
47963
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: PostgreSQL Global Development Group (2024), *PostgreSQL 16 Documentation \xA711.2 (Index Types)*; Heroku Postgres Team (2018), *Efficient Use of PostgreSQL Indexes*. (Duplicate indexes silently double write cost without read benefit \u2014 Postgres does not warn.)",
47964
- aiSpecific: false
49729
+ ratio: 51016.19,
49730
+ precision: 0.8095,
49731
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49732
+ verdict: "USEFUL",
49733
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=17, FP=4, P=81.0%, FPR=0.00%, lift=51016.19. v7 was USEFUL (TP=14, FP=3, lift=50348.67). v8 was USEFUL (TP=3, FP=1).",
49734
+ aiSpecific: true,
49735
+ _v7Verdict: "USEFUL",
49736
+ _v7Lift: 50348.67,
49737
+ _v7Recall: 1e-4,
49738
+ _v7FpRate: 0,
49739
+ _v7Precision: 0.8235,
49740
+ _v8Verdict: "USEFUL",
49741
+ _v8Lift: 51500.25
47965
49742
  },
47966
- "db/missing-not-null": {
49743
+ "test/fake-placeholder": {
49744
+ recall: 51e-4,
49745
+ fpRate: 15e-4,
49746
+ ratio: 547.47,
49747
+ precision: 0.8014,
49748
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49749
+ verdict: "USEFUL",
49750
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1489, FP=369, P=80.1%, FPR=0.15%, lift=547.47. v7 was USEFUL (TP=1259, FP=344, lift=418.76). v8 was USEFUL (TP=230, FP=25).",
49751
+ aiSpecific: true,
49752
+ _v7Verdict: "USEFUL",
49753
+ _v7Lift: 418.76,
49754
+ _v7Recall: 53e-4,
49755
+ _v7FpRate: 19e-4,
49756
+ _v7Precision: 0.7854,
49757
+ _v8Verdict: "USEFUL",
49758
+ _v8Lift: 2477.4
49759
+ },
49760
+ "test/missing-edge-case": {
47967
49761
  recall: 0,
47968
49762
  fpRate: 0,
47969
49763
  ratio: 0,
47970
49764
  precision: 0,
47971
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49765
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47972
49766
  verdict: "DORMANT",
47973
- defaultOff: true,
47974
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: PostgreSQL Global Development Group (2024), *PostgreSQL 16 Documentation \xA75.4.1 (Check Constraints)*; Kleppmann, M. (2017), *Designing Data-Intensive Applications*, O'Reilly, ch.4 (NULL semantics in RDBMS). (Required-identifier columns without NOT NULL produce silent NULL inserts \u2014 canonical AI SQL smell.)",
47975
- aiSpecific: false
49767
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49768
+ aiSpecific: true,
49769
+ _v7Verdict: "DORMANT",
49770
+ _v7Lift: 0,
49771
+ _v7Recall: 0,
49772
+ _v7FpRate: 0,
49773
+ _v7Precision: 0,
49774
+ _v8Verdict: "DORMANT",
49775
+ _v8Lift: 1,
49776
+ defaultOff: true
47976
49777
  },
47977
- "db/enum-sprawl": {
49778
+ "test/weak-assertion": {
49779
+ recall: 0.0417,
49780
+ fpRate: 67e-4,
49781
+ ratio: 131.62,
49782
+ precision: 0.8793,
49783
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49784
+ verdict: "USEFUL",
49785
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=12263, FP=1684, P=87.9%, FPR=0.67%, lift=131.62. v7 was USEFUL (TP=9906, FP=1583, lift=99.90). v8 was USEFUL (TP=2357, FP=101).",
49786
+ aiSpecific: true,
49787
+ _v7Verdict: "USEFUL",
49788
+ _v7Lift: 99.9,
49789
+ _v7Recall: 0.0418,
49790
+ _v7FpRate: 86e-4,
49791
+ _v7Precision: 0.8622,
49792
+ _v8Verdict: "USEFUL",
49793
+ _v8Lift: 651.94
49794
+ },
49795
+ "ts/enum-vs-as-const": {
47978
49796
  recall: 0,
47979
49797
  fpRate: 0,
47980
- ratio: 0,
49798
+ ratio: 1,
47981
49799
  precision: 0,
47982
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49800
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47983
49801
  verdict: "DORMANT",
47984
- defaultOff: true,
47985
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: PostgreSQL Global Development Group (2024), *PostgreSQL 16 Documentation \xA78.7 (Enumerated Types)*; Borkowski, A. (2022), *PostgreSQL enum types: when to use them and when not to*, pgconfig.org. (Enums with >12 values are brittle to extend and hard to localize \u2014 lookup table is the standard replacement.)",
47986
- aiSpecific: false
49802
+ _calibrationNote: "v0.19: new rule (Uses `enum` \u2014 modern TS prefers `as const`). Not yet calibrated. Scheduled for v9 calibration.",
49803
+ aiSpecific: true,
49804
+ _v7Verdict: "DORMANT",
49805
+ _v7Lift: 1,
49806
+ _v7Recall: 0,
49807
+ _v7FpRate: 0,
49808
+ _v7Precision: 0,
49809
+ _v8Verdict: "DORMANT",
49810
+ _v8Lift: 1,
49811
+ defaultOff: true
47987
49812
  },
47988
- "db/naming-inconsistency": {
49813
+ "ts/excessive-type-assertion": {
47989
49814
  recall: 0,
47990
49815
  fpRate: 0,
47991
- ratio: 0,
49816
+ ratio: 1,
47992
49817
  precision: 0,
47993
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49818
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
47994
49819
  verdict: "DORMANT",
47995
- defaultOff: true,
47996
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: PostgreSQL Global Development Group (2024), *PostgreSQL 16 Documentation \xA74.1.1 (Identifiers and Key Words)*; pgsql-hackers (2018), *Re: snake_case vs camelCase in Postgres identifiers*. (snake_case is the Postgres convention; mixing styles in the same schema breaks ORM generators.)",
47997
- aiSpecific: false
49820
+ _calibrationNote: "v0.19: new rule (Function with >3 `as` assertions \u2014 AI fighting the type system). Not yet calibrated. Scheduled for v9 calibration.",
49821
+ aiSpecific: true,
49822
+ _v7Verdict: "DORMANT",
49823
+ _v7Lift: 1,
49824
+ _v7Recall: 0,
49825
+ _v7FpRate: 0,
49826
+ _v7Precision: 0,
49827
+ _v8Verdict: "DORMANT",
49828
+ _v8Lift: 1,
49829
+ defaultOff: true
47998
49830
  },
47999
- "db/sql-concat": {
49831
+ "ts/import-type-misuse": {
48000
49832
  recall: 0,
48001
49833
  fpRate: 0,
48002
- ratio: 0,
49834
+ ratio: 1,
48003
49835
  precision: 0,
48004
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49836
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48005
49837
  verdict: "DORMANT",
48006
- defaultOff: true,
48007
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: OWASP Foundation (2021), *OWASP Top 10 \u2014 A03:2021 Injection*, https://owasp.org/Top10/A03_2021-Injection/; OWASP Foundation (2017), *SQL Injection Prevention Cheat Sheet*. (Template-literal SQL with ${...} interpolation is the #1 SQL injection vector in AI-generated TypeScript code.)",
48008
- aiSpecific: true
49838
+ _calibrationNote: "v0.19: new rule (Inline `import { type X }` \u2014 prefer separate `import type`). Not yet calibrated. Scheduled for v9 calibration.",
49839
+ aiSpecific: true,
49840
+ _v7Verdict: "DORMANT",
49841
+ _v7Lift: 1,
49842
+ _v7Recall: 0,
49843
+ _v7FpRate: 0,
49844
+ _v7Precision: 0,
49845
+ _v8Verdict: "DORMANT",
49846
+ _v8Lift: 1,
49847
+ defaultOff: true
48009
49848
  },
48010
- "dead/unused-import": {
49849
+ "ts/never-vs-unknown": {
48011
49850
  recall: 0,
48012
49851
  fpRate: 0,
48013
- ratio: 0,
49852
+ ratio: 1,
48014
49853
  precision: 0,
48015
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49854
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48016
49855
  verdict: "DORMANT",
48017
- defaultOff: true,
48018
- _calibrationNote: "v0.18.5 ship \u2014 not in v7 per-rule table. Default-off until v8 calibration data lands. The first of 5 planned `dead/*` rules (this one + dead/unused-local + dead/unused-parameter + dead/dead-branch + dead/unreachable). The pattern is the canonical AI-iteration rot: the model adds an import when introducing a feature, then rewrites the function later without cleaning up. Most real-world tsconfig.json files have `noUnusedLocals: false`, so tsc never fires.",
48019
- aiSpecific: true
49856
+ _calibrationNote: "v0.19: new rule (Return type `never` but body has no throw/loop/exit). Not yet calibrated. Scheduled for v9 calibration.",
49857
+ aiSpecific: true,
49858
+ _v7Verdict: "DORMANT",
49859
+ _v7Lift: 1,
49860
+ _v7Recall: 0,
49861
+ _v7FpRate: 0,
49862
+ _v7Precision: 0,
49863
+ _v8Verdict: "DORMANT",
49864
+ _v8Lift: 1,
49865
+ defaultOff: true
48020
49866
  },
48021
- "dead/unused-local": {
49867
+ "ts/optional-chain-overuse": {
48022
49868
  recall: 0,
48023
49869
  fpRate: 0,
48024
- ratio: 0,
49870
+ ratio: 1,
48025
49871
  precision: 0,
48026
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49872
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48027
49873
  verdict: "DORMANT",
48028
- defaultOff: true,
48029
- _calibrationNote: "v0.18.5b ship \u2014 not in v7 per-rule table. Default-off until v8 calibration data lands. The second of 5 planned `dead/*` rules. Muchnick 1997 Ch. 13 'liveness analysis' (textbook compiler optimization).",
48030
- aiSpecific: true
49874
+ _calibrationNote: "v0.19: new rule (Optional chain depth >= 5 \u2014 AI chains ?. rather than narrowing). Not yet calibrated. Scheduled for v9 calibration.",
49875
+ aiSpecific: true,
49876
+ _v7Verdict: "DORMANT",
49877
+ _v7Lift: 1,
49878
+ _v7Recall: 0,
49879
+ _v7FpRate: 0,
49880
+ _v7Precision: 0,
49881
+ _v8Verdict: "DORMANT",
49882
+ _v8Lift: 1,
49883
+ defaultOff: true
48031
49884
  },
48032
- "dead/unused-parameter": {
49885
+ "typo/calc-fontsize": {
48033
49886
  recall: 0,
48034
49887
  fpRate: 0,
48035
49888
  ratio: 0,
48036
49889
  precision: 0,
48037
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49890
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48038
49891
  verdict: "DORMANT",
48039
- defaultOff: true,
48040
- _calibrationNote: "v0.18.5b ship \u2014 not in v7 per-rule table. Default-off until v8 calibration data lands. The third of 5 planned `dead/*` rules. AI agents add parameters when introducing features, then rewrite the function without removing parameters the new code does not need.",
48041
- aiSpecific: true
49892
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49893
+ aiSpecific: false,
49894
+ _v7Verdict: "DORMANT",
49895
+ _v7Lift: 0,
49896
+ _v7Recall: 0,
49897
+ _v7FpRate: 0,
49898
+ _v7Precision: 0,
49899
+ _v8Verdict: "DORMANT",
49900
+ _v8Lift: 1,
49901
+ defaultOff: true
48042
49902
  },
48043
- "dead/dead-branch": {
49903
+ "typo/calc-raw-px": {
48044
49904
  recall: 0,
48045
49905
  fpRate: 0,
48046
- ratio: 0,
48047
- precision: 0,
48048
- lastCalibratedAt: "2026-06-30T00:00:00Z",
48049
- verdict: "DORMANT",
48050
- defaultOff: true,
48051
- _calibrationNote: "v0.18.5b ship \u2014 not in v7 per-rule table. Default-off until v8 calibration data lands. The fourth of 5 planned `dead/*` rules. AI-iteration signature: feature flag toggled to a constant, or wrapper from a previous refactor.",
48052
- aiSpecific: true
49906
+ ratio: 210066.67,
49907
+ precision: 0.8333,
49908
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49909
+ verdict: "USEFUL",
49910
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=5, FP=1, P=83.3%, FPR=0.00%, lift=210066.67. v7 was USEFUL (TP=3, FP=1, lift=137559.75). v8 was USEFUL (TP=2, FP=0).",
49911
+ aiSpecific: false,
49912
+ _v7Verdict: "USEFUL",
49913
+ _v7Lift: 137559.75,
49914
+ _v7Recall: 0,
49915
+ _v7FpRate: 0,
49916
+ _v7Precision: 0.75,
49917
+ _v8Verdict: "USEFUL",
49918
+ _v8Lift: 99999
48053
49919
  },
48054
- "dead/unreachable": {
49920
+ "typo/clamp-offscale": {
48055
49921
  recall: 0,
48056
49922
  fpRate: 0,
48057
49923
  ratio: 0,
48058
49924
  precision: 0,
48059
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49925
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48060
49926
  verdict: "DORMANT",
48061
- defaultOff: true,
48062
- _calibrationNote: "v0.18.5b ship \u2014 not in v7 per-rule table. Default-off until v8 calibration data lands. The fifth of 5 planned `dead/*` rules. AI-iteration signature: model added an early return for a new error path, then forgot the rest of the function body was still sitting below it.",
48063
- aiSpecific: true
49927
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49928
+ aiSpecific: false,
49929
+ _v7Verdict: "DORMANT",
49930
+ _v7Lift: 0,
49931
+ _v7Recall: 0,
49932
+ _v7FpRate: 0,
49933
+ _v7Precision: 0,
49934
+ _v8Verdict: "DORMANT",
49935
+ _v8Lift: 1,
49936
+ defaultOff: true
48064
49937
  },
48065
- "docs/stale-package-reference": {
49938
+ "typo/math-button-label-uniformity": {
49939
+ recall: 1e-4,
49940
+ fpRate: 1e-4,
49941
+ ratio: 6894.19,
49942
+ precision: 0.629,
49943
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49944
+ verdict: "USEFUL",
49945
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=39, FP=23, P=62.9%, FPR=0.01%, lift=6894.19. v7 was USEFUL (TP=37, FP=21, lift=5571.66). v8 was USEFUL (TP=2, FP=2).",
49946
+ aiSpecific: false,
49947
+ _v7Verdict: "USEFUL",
49948
+ _v7Lift: 5571.66,
49949
+ _v7Recall: 2e-4,
49950
+ _v7FpRate: 1e-4,
49951
+ _v7Precision: 0.6379,
49952
+ _v8Verdict: "USEFUL",
49953
+ _v8Lift: 17166.75
49954
+ },
49955
+ "typo/math-cta-vocabulary": {
48066
49956
  recall: 0,
48067
49957
  fpRate: 0,
48068
49958
  ratio: 0,
48069
49959
  precision: 0,
48070
- lastCalibratedAt: "2026-06-30T00:00:00Z",
49960
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48071
49961
  verdict: "DORMANT",
48072
- defaultOff: true,
48073
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: Meng, M. et al. (2020), *The Cost of Fixing Code Documentation: An Empirical Study on Open-Source Software*, ICSE 2020; Aghajani, E. et al. (2019), *Software documentation: a practitioners' perspective*, ICSE 2019. (Doc references to undeclared packages are copy-paste rot from previous projects \u2014 high-signal doc drift.)",
48074
- aiSpecific: false
49962
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
49963
+ aiSpecific: true,
49964
+ _v7Verdict: "DORMANT",
49965
+ _v7Lift: 0,
49966
+ _v7Recall: 0,
49967
+ _v7FpRate: 0,
49968
+ _v7Precision: 0,
49969
+ _v8Verdict: "DORMANT",
49970
+ _v8Lift: 1,
49971
+ defaultOff: true
48075
49972
  },
48076
- "docs/stale-function-reference": {
48077
- recall: 0,
49973
+ "typo/placeholder-text": {
49974
+ recall: 1e-4,
48078
49975
  fpRate: 0,
48079
- ratio: 0,
48080
- precision: 0,
48081
- lastCalibratedAt: "2026-06-30T00:00:00Z",
48082
- verdict: "DORMANT",
48083
- defaultOff: true,
48084
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: Meng, M. et al. (2020), *The Cost of Fixing Code Documentation*; Chen, Z. et al. (2022), *An Empirical Study on Code Documentation Adequacy*, MSR 2022. (Doc callouts to non-existent exports \u2014 readers copy-paste and hit a runtime error.)",
48085
- aiSpecific: false
49976
+ ratio: 17330.5,
49977
+ precision: 0.6875,
49978
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49979
+ verdict: "USEFUL",
49980
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=22, FP=10, P=68.8%, FPR=0.00%, lift=17330.50. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=22, FP=10).",
49981
+ aiSpecific: false,
49982
+ _v7Verdict: "DORMANT",
49983
+ _v7Lift: 1,
49984
+ _v7Recall: 0,
49985
+ _v7FpRate: 0,
49986
+ _v7Precision: 0,
49987
+ _v8Verdict: "USEFUL",
49988
+ _v8Lift: 4720.86
48086
49989
  },
48087
- "docs/expired-code-example": {
49990
+ "visual/arbitrary-escape": {
49991
+ recall: 26e-4,
49992
+ fpRate: 7e-4,
49993
+ ratio: 1219.67,
49994
+ precision: 0.8177,
49995
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
49996
+ verdict: "USEFUL",
49997
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=758, FP=169, P=81.8%, FPR=0.07%, lift=1219.67. v7 was USEFUL (TP=600, FP=169, lift=846.78). v8 was USEFUL (TP=158, FP=0).",
49998
+ aiSpecific: true,
49999
+ _v7Verdict: "USEFUL",
50000
+ _v7Lift: 846.78,
50001
+ _v7Recall: 25e-4,
50002
+ _v7FpRate: 9e-4,
50003
+ _v7Precision: 0.7802,
50004
+ _v8Verdict: "USEFUL",
50005
+ _v8Lift: 99999
50006
+ },
50007
+ "visual/clamp-soup": {
48088
50008
  recall: 0,
48089
50009
  fpRate: 0,
48090
50010
  ratio: 0,
48091
50011
  precision: 0,
48092
- lastCalibratedAt: "2026-06-30T00:00:00Z",
50012
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48093
50013
  verdict: "DORMANT",
48094
- defaultOff: true,
48095
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: Parnas, D.L. (2010), *Precise Documentation: The Key to Better Software*, Springer (canonical doc-correctness argument); Chen, Z. et al. (2022). (Fenced code examples with broken imports erode trust in the entire docs site.)",
48096
- aiSpecific: false
50014
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
50015
+ aiSpecific: true,
50016
+ _v7Verdict: "DORMANT",
50017
+ _v7Lift: 0,
50018
+ _v7Recall: 0,
50019
+ _v7FpRate: 0,
50020
+ _v7Precision: 0,
50021
+ _v8Verdict: "DORMANT",
50022
+ _v8Lift: 1,
50023
+ defaultOff: true
48097
50024
  },
48098
- "docs/broken-link": {
50025
+ "visual/generic-centering": {
48099
50026
  recall: 0,
48100
50027
  fpRate: 0,
48101
50028
  ratio: 0,
48102
50029
  precision: 0,
48103
- lastCalibratedAt: "2026-06-30T00:00:00Z",
50030
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48104
50031
  verdict: "DORMANT",
48105
- defaultOff: true,
48106
- _calibrationNote: "v0.17.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: Nielsen, J. (2000), *Designing Web Usability*, New Riders (broken links as a top-5 trust erosion signal); Google Search Central (2024), *Crawl errors documentation*, https://developers.google.com/search/docs/crawling-indexing. (Broken internal links cost SEO crawl budget and reader trust.)",
48107
- aiSpecific: false
50032
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
50033
+ aiSpecific: true,
50034
+ _v7Verdict: "DORMANT",
50035
+ _v7Lift: 0,
50036
+ _v7Recall: 0,
50037
+ _v7FpRate: 0,
50038
+ _v7Precision: 0,
50039
+ _v8Verdict: "DORMANT",
50040
+ _v8Lift: 1,
50041
+ defaultOff: true
48108
50042
  },
48109
- "security/eval": {
48110
- recall: 0,
50043
+ "visual/inline-style-dominance": {
50044
+ recall: 0.0103,
50045
+ fpRate: 52e-4,
50046
+ ratio: 134.59,
50047
+ precision: 0.6983,
50048
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50049
+ verdict: "USEFUL",
50050
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=3028, FP=1308, P=69.8%, FPR=0.52%, lift=134.59. v7 was USEFUL (TP=2303, FP=1221, lift=98.17). v8 was USEFUL (TP=725, FP=87).",
50051
+ aiSpecific: false,
50052
+ _v7Verdict: "USEFUL",
50053
+ _v7Lift: 98.17,
50054
+ _v7Recall: 97e-4,
50055
+ _v7FpRate: 67e-4,
50056
+ _v7Precision: 0.6535,
50057
+ _v8Verdict: "USEFUL",
50058
+ _v8Lift: 704.71
50059
+ },
50060
+ "visual/math-color-cluster": {
50061
+ recall: 2e-4,
48111
50062
  fpRate: 0,
48112
- ratio: 0,
48113
- precision: 0,
48114
- lastCalibratedAt: "2026-06-30T00:00:00Z",
48115
- verdict: "DORMANT",
48116
- defaultOff: true,
48117
- _calibrationNote: "v0.16.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: OWASP Foundation (2021), *OWASP Top 10 \u2014 A03:2021 Injection*; Mozilla Developer Network (2024), *eval() \u2014 MDN Web Docs*, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval (Security Considerations). (eval() and new Function() are RCE vectors if the input is ever attacker-controlled.)",
48118
- aiSpecific: false
50063
+ ratio: 46814.86,
50064
+ precision: 0.9286,
50065
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50066
+ verdict: "USEFUL",
50067
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=65, FP=5, P=92.9%, FPR=0.00%, lift=46814.86. v7 was USEFUL (TP=58, FP=5, lift=33771.28). v8 was USEFUL (TP=7, FP=0).",
50068
+ aiSpecific: true,
50069
+ _v7Verdict: "USEFUL",
50070
+ _v7Lift: 33771.28,
50071
+ _v7Recall: 2e-4,
50072
+ _v7FpRate: 0,
50073
+ _v7Precision: 0.9206,
50074
+ _v8Verdict: "USEFUL",
50075
+ _v8Lift: 99999
48119
50076
  },
48120
- "security/localstorage-token": {
50077
+ "visual/math-default-font": {
50078
+ recall: 16e-4,
50079
+ fpRate: 3e-4,
50080
+ ratio: 3035.16,
50081
+ precision: 0.8669,
50082
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50083
+ verdict: "USEFUL",
50084
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=469, FP=72, P=86.7%, FPR=0.03%, lift=3035.16. v7 was USEFUL (TP=313, FP=71, lift=2105.64). v8 was USEFUL (TP=156, FP=1).",
50085
+ aiSpecific: true,
50086
+ _v7Verdict: "USEFUL",
50087
+ _v7Lift: 2105.64,
50088
+ _v7Recall: 13e-4,
50089
+ _v7FpRate: 4e-4,
50090
+ _v7Precision: 0.8151,
50091
+ _v8Verdict: "USEFUL",
50092
+ _v8Lift: 68229.63
50093
+ },
50094
+ "visual/math-font-entropy": {
50095
+ recall: 52e-4,
50096
+ fpRate: 1e-3,
50097
+ ratio: 869.95,
50098
+ precision: 0.8593,
50099
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50100
+ verdict: "USEFUL",
50101
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1521, FP=249, P=85.9%, FPR=0.10%, lift=869.95. v7 was USEFUL (TP=1067, FP=248, lift=600.09). v8 was USEFUL (TP=454, FP=1).",
50102
+ aiSpecific: true,
50103
+ _v7Verdict: "USEFUL",
50104
+ _v7Lift: 600.09,
50105
+ _v7Recall: 45e-4,
50106
+ _v7FpRate: 14e-4,
50107
+ _v7Precision: 0.8114,
50108
+ _v8Verdict: "USEFUL",
50109
+ _v8Lift: 68516.08
50110
+ },
50111
+ "visual/math-gradient-hue-rotation": {
48121
50112
  recall: 0,
48122
50113
  fpRate: 0,
48123
50114
  ratio: 0,
48124
50115
  precision: 0,
48125
- lastCalibratedAt: "2026-06-30T00:00:00Z",
50116
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48126
50117
  verdict: "DORMANT",
48127
- defaultOff: true,
48128
- _calibrationNote: "v0.16.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: OWASP Foundation (2021), *OWASP Top 10 \u2014 A07:2021 Identification and Authentication Failures*; OWASP Foundation (2022), *HTML5 Security Cheat Sheet \xA72.1 (Local Storage)*. (JWT/access/refresh tokens in localStorage are readable by any XSS payload \u2014 issue as httpOnly cookies instead.)",
48129
- aiSpecific: false
50118
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
50119
+ aiSpecific: true,
50120
+ _v7Verdict: "DORMANT",
50121
+ _v7Lift: 0,
50122
+ _v7Recall: 0,
50123
+ _v7FpRate: 0,
50124
+ _v7Precision: 0,
50125
+ _v8Verdict: "DORMANT",
50126
+ _v8Lift: 1,
50127
+ defaultOff: true
48130
50128
  },
48131
- "security/target-blank-no-noopener": {
48132
- recall: 0,
50129
+ "visual/math-rounded-entropy": {
50130
+ recall: 38e-4,
50131
+ fpRate: 2e-4,
50132
+ ratio: 3785.45,
50133
+ precision: 0.9461,
50134
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50135
+ verdict: "USEFUL",
50136
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=1105, FP=63, P=94.6%, FPR=0.02%, lift=3785.45. v7 was USEFUL (TP=876, FP=62, lift=2762.74). v8 was USEFUL (TP=229, FP=1).",
50137
+ aiSpecific: true,
50138
+ _v7Verdict: "USEFUL",
50139
+ _v7Lift: 2762.74,
50140
+ _v7Recall: 37e-4,
50141
+ _v7FpRate: 3e-4,
50142
+ _v7Precision: 0.9339,
50143
+ _v8Verdict: "USEFUL",
50144
+ _v8Lift: 68368.45
50145
+ },
50146
+ "visual/math-spacing-entropy": {
50147
+ recall: 18e-4,
50148
+ fpRate: 4e-4,
50149
+ ratio: 1898.99,
50150
+ precision: 0.8287,
50151
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50152
+ verdict: "USEFUL",
50153
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=532, FP=110, P=82.9%, FPR=0.04%, lift=1898.99. v7 was USEFUL (TP=425, FP=109, lift=1339.22). v8 was USEFUL (TP=107, FP=1).",
50154
+ aiSpecific: true,
50155
+ _v7Verdict: "USEFUL",
50156
+ _v7Lift: 1339.22,
50157
+ _v7Recall: 18e-4,
50158
+ _v7FpRate: 6e-4,
50159
+ _v7Precision: 0.7959,
50160
+ _v8Verdict: "USEFUL",
50161
+ _v8Lift: 68031.19
50162
+ },
50163
+ "visual/naturalness-anomaly": {
50164
+ recall: 0.1729,
50165
+ fpRate: 0.0687,
50166
+ ratio: 10.86,
50167
+ precision: 0.746,
50168
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50169
+ verdict: "USEFUL",
50170
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=50852, FP=17316, P=74.6%, FPR=6.87%, lift=10.86. v7 was USEFUL (TP=39319, FP=11382, lift=12.50). v8 was USEFUL (TP=11533, FP=5934).",
50171
+ aiSpecific: true,
50172
+ _v7Verdict: "USEFUL",
50173
+ _v7Lift: 12.5,
50174
+ _v7Recall: 0.1659,
50175
+ _v7FpRate: 0.0621,
50176
+ _v7Precision: 0.7755,
50177
+ _v8Verdict: "USEFUL",
50178
+ _v8Lift: 7.64
50179
+ },
50180
+ "visual/radius-scale-violation": {
50181
+ recall: 4e-4,
48133
50182
  fpRate: 0,
48134
- ratio: 0,
48135
- precision: 0,
48136
- lastCalibratedAt: "2026-06-30T00:00:00Z",
48137
- verdict: "DORMANT",
48138
- defaultOff: true,
48139
- _calibrationNote: 'v0.16.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: OWASP Foundation (2022), *HTML5 Security Cheat Sheet \xA72.3 (Tabnabbing)*; WHATWG (2024), *HTML Living Standard \u2014 Link types: noopener*. (target="_blank" without rel="noopener" lets the linked page control window.opener \u2014 reverse tabnabbing.)',
48140
- aiSpecific: false
50183
+ ratio: 82131.33,
50184
+ precision: 0.9774,
50185
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50186
+ verdict: "USEFUL",
50187
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=130, FP=3, P=97.7%, FPR=0.00%, lift=82131.33. v7 was USEFUL (TP=96, FP=3, lift=59285.01). v8 was USEFUL (TP=34, FP=0). v0.19 default-on (explicit defaultOff: false): P 97.7% / 82k lift \u2014 UI consistency.",
50188
+ aiSpecific: false,
50189
+ _v7Verdict: "USEFUL",
50190
+ _v7Lift: 59285.01,
50191
+ _v7Recall: 4e-4,
50192
+ _v7FpRate: 0,
50193
+ _v7Precision: 0.9697,
50194
+ _v8Verdict: "USEFUL",
50195
+ _v8Lift: 99999,
50196
+ defaultOff: false
48141
50197
  },
48142
- "wcag/missing-alt": {
50198
+ "visual/spacing-scale-violation": {
50199
+ recall: 86e-4,
50200
+ fpRate: 28e-4,
50201
+ ratio: 273.97,
50202
+ precision: 0.7792,
50203
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50204
+ verdict: "USEFUL",
50205
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=2531, FP=717, P=77.9%, FPR=0.28%, lift=273.97. v7 was USEFUL (TP=1981, FP=717, lift=187.83). v8 was USEFUL (TP=550, FP=0).",
50206
+ aiSpecific: false,
50207
+ _v7Verdict: "USEFUL",
50208
+ _v7Lift: 187.83,
50209
+ _v7Recall: 84e-4,
50210
+ _v7FpRate: 39e-4,
50211
+ _v7Precision: 0.7342,
50212
+ _v8Verdict: "USEFUL",
50213
+ _v8Lift: 99999
50214
+ },
50215
+ "wcag/dragging-movements": {
48143
50216
  recall: 0,
48144
50217
  fpRate: 0,
48145
- ratio: 0,
48146
- precision: 0,
48147
- lastCalibratedAt: "2026-06-30T00:00:00Z",
48148
- verdict: "DORMANT",
48149
- defaultOff: true,
48150
- _calibrationNote: 'v0.16.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: W3C (2023), *Web Content Accessibility Guidelines (WCAG) 2.2 \u2014 1.1.1 Non-text Content*, https://www.w3.org/TR/WCAG22/. (Every <img> needs alt text. Decorative: alt="". Informative: describe the image.)',
48151
- aiSpecific: false
50218
+ ratio: 33610.67,
50219
+ precision: 0.4,
50220
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50221
+ verdict: "OK",
50222
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=2, FP=3, P=40.0%, FPR=0.00%, lift=33610.67. v7 was OK (TP=2, FP=3, lift=24455.07). v8 was DORMANT (TP=0, FP=0).",
50223
+ aiSpecific: false,
50224
+ _v7Verdict: "OK",
50225
+ _v7Lift: 24455.07,
50226
+ _v7Recall: 0,
50227
+ _v7FpRate: 0,
50228
+ _v7Precision: 0.4,
50229
+ _v8Verdict: "DORMANT",
50230
+ _v8Lift: 1
48152
50231
  },
48153
- "typo/placeholder-text": {
50232
+ "wcag/focus-appearance": {
50233
+ recall: 28e-4,
50234
+ fpRate: 1e-4,
50235
+ ratio: 8397.24,
50236
+ precision: 0.966,
50237
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50238
+ verdict: "USEFUL",
50239
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=825, FP=29, P=96.6%, FPR=0.01%, lift=8397.24. v7 was USEFUL (TP=612, FP=24, lift=7353.82). v8 was USEFUL (TP=213, FP=5).",
50240
+ aiSpecific: false,
50241
+ _v7Verdict: "USEFUL",
50242
+ _v7Lift: 7353.82,
50243
+ _v7Recall: 26e-4,
50244
+ _v7FpRate: 1e-4,
50245
+ _v7Precision: 0.9623,
50246
+ _v8Verdict: "USEFUL",
50247
+ _v8Lift: 13418.41
50248
+ },
50249
+ "wcag/focus-obscured": {
50250
+ recall: 33e-4,
50251
+ fpRate: 7e-4,
50252
+ ratio: 1214.21,
50253
+ precision: 0.8478,
50254
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50255
+ verdict: "USEFUL",
50256
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=980, FP=176, P=84.8%, FPR=0.07%, lift=1214.21. v7 was USEFUL (TP=797, FP=175, lift=859.38). v8 was USEFUL (TP=183, FP=1).",
50257
+ aiSpecific: false,
50258
+ _v7Verdict: "USEFUL",
50259
+ _v7Lift: 859.38,
50260
+ _v7Recall: 34e-4,
50261
+ _v7FpRate: 1e-3,
50262
+ _v7Precision: 0.82,
50263
+ _v8Verdict: "USEFUL",
50264
+ _v8Lift: 68293.81
50265
+ },
50266
+ "wcag/missing-alt": {
50267
+ recall: 11e-4,
50268
+ fpRate: 7e-4,
50269
+ ratio: 935.33,
50270
+ precision: 0.6456,
50271
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
50272
+ verdict: "USEFUL",
50273
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): v7+v8 combined corpus (252080 neg + 294178 pos). v8.5 TP=317, FP=174, P=64.6%, FPR=0.07%, lift=935.33. v7 was DORMANT (TP=0, FP=0, lift=1.00). v8 was USEFUL (TP=317, FP=174).",
50274
+ aiSpecific: false,
50275
+ _v7Verdict: "DORMANT",
50276
+ _v7Lift: 1,
50277
+ _v7Recall: 0,
50278
+ _v7FpRate: 0,
50279
+ _v7Precision: 0,
50280
+ _v8Verdict: "USEFUL",
50281
+ _v8Lift: 254.79
50282
+ },
50283
+ "wcag/target-size": {
48154
50284
  recall: 0,
48155
50285
  fpRate: 0,
48156
50286
  ratio: 0,
48157
50287
  precision: 0,
48158
- lastCalibratedAt: "2026-06-30T00:00:00Z",
50288
+ lastCalibratedAt: "2026-07-01T00:00:00Z",
48159
50289
  verdict: "DORMANT",
48160
- defaultOff: true,
48161
- _calibrationNote: "v0.16.0 ship \u2014 not in v7 per-rule table. Default-off until calibration data lands. Backed by: Nielsen, J. (2000), *Designing Web Usability* (placeholder copy as a top-10 trust erosion signal); Krug, S. (2000), *Don't Make Me Think*, 2nd ed. (TODO/placeholder text in shipped UI signals incomplete work.)",
48162
- aiSpecific: false
50290
+ _calibrationNote: "v8.5 calibration (v0.18.9, 2026-07-01): rule did not fire on v8.5 corpus (252080 neg + 294178 pos = 546258 files). Preserved v7 data. v7 verdict=DORMANT, v7 lift=0, v7 recall=0, v7 FPR=0, v7 precision=0.",
50291
+ aiSpecific: false,
50292
+ _v7Verdict: "DORMANT",
50293
+ _v7Lift: 0,
50294
+ _v7Recall: 0,
50295
+ _v7FpRate: 0,
50296
+ _v7Precision: 0,
50297
+ _v8Verdict: "DORMANT",
50298
+ _v8Lift: 1,
50299
+ defaultOff: true
48163
50300
  }
48164
50301
  };
48165
50302
  }
@@ -48229,7 +50366,6 @@ async function scanFile(filePath, config, registry, cwd = process.cwd()) {
48229
50366
  ".kt",
48230
50367
  ".kts",
48231
50368
  ".dart",
48232
- ".rs",
48233
50369
  ".cpp",
48234
50370
  ".cc",
48235
50371
  ".cxx",
@@ -55058,7 +57194,24 @@ var RULE_HINTS = {
55058
57194
  "docs/stale-package-reference": "Update the doc to reference an installed package, or add the package to package.json. Copy-pasted install commands from a previous project are the #1 doc-drift failure mode.",
55059
57195
  "docs/stale-function-reference": "Rename the doc reference to match a current export, or add a wrapper export. Stale function callouts in tutorials cost readers 10+ minutes of debugging.",
55060
57196
  "docs/expired-code-example": "Update the example to use a declared dependency, or add the package to package.json. A copy-pasteable example that does not install erodes trust in the whole docs site.",
55061
- "docs/broken-link": "Create the file or fix the link target. On a public docs site, broken links erode trust more than stale copy."
57197
+ "docs/broken-link": "Create the file or fix the link target. On a public docs site, broken links erode trust more than stale copy.",
57198
+ // v0.18.9 — Rust-specific rules (tree-sitter-backed)
57199
+ "rust/unused-pub-fn": "Remove the function or call it. Rust's compiler doesn't warn on `pub fn` references unless `#![warn(dead_code)]` is set at the crate root. AI agents leave these behind during iterative refactors.",
57200
+ "rust/unwrap-in-production": "Replace `.unwrap()` / `.expect()` with `?` (early-return on Err), `.map_err(...)` for error conversion, or an explicit `match`. They panic in production; reserve them for `#[cfg(test)]` or `#[test]` scopes.",
57201
+ "rust/todo-macro": "Implement the function body or remove the stub. `todo!()` / `unimplemented!()` both expand to `panic!()`, so a hit at runtime crashes the program. Move them into a `#[cfg(test)] mod tests` block to suppress.",
57202
+ "rust/stringly-typed": 'Replace the `String` / `&str` parameter with the typed enum that already exists in the file. Stringly-typed APIs lose type information at the boundary \u2014 typos (`"Click"` vs `"click"`) only fail at runtime.',
57203
+ // v0.19.0 — TypeScript-specific rules
57204
+ "ts/optional-chain-overuse": "Break long `?.` chains with an intermediate variable or guard clause (`if (!value) return;`). Long chains hide the type-narrowing that should be explicit. AI agents chain `?.` to avoid null checks.",
57205
+ "ts/enum-vs-as-const": "Replace `enum Foo { A, B }` with `const Foo = { A: 'A', B: 'B' } as const` (or `const Foo = ['A', 'B'] as const`). Modern TS style guides (Google, TS-eslint) prefer `as const` because enums have surprising runtime semantics.",
57206
+ "ts/import-type-misuse": "Use `import type { X } from '...'` instead of `import { type X } from '...'`. The split form is more common in real codebases and makes the type-only intent unambiguous.",
57207
+ "ts/never-vs-unknown": "The `never` return type means 'this function never returns'. Reserve it for functions that always throw, always loop, or always exit. For 'impossible' branches, use a concrete type (`void`, `Error`, `unknown`) and an exhaustive check.",
57208
+ "ts/excessive-type-assertion": "More than 3 `as` assertions in one function is a strong signal that the type is wrong, not the code. Fix the type definition (or use a type guard) instead of bypassing the type system.",
57209
+ // v0.19.0 — Go-specific rules
57210
+ "go/error-wrap-without-context": 'Use `fmt.Errorf("opening config: %w", err)` not `fmt.Errorf("error: %w", err)`. Real Go errors include the failing operation. See golang.org/wiki/CodeReviewComments#error-strings.',
57211
+ "go/struct-tag-inconsistency": 'Pick one tag style per struct. If most fields are `json:"foo"`, this field should be too. Real Go code maintains consistency within a struct (or within a package).',
57212
+ "go/nil-slice-vs-empty": "Either declare as `var x = []int{}` or assign with `make([]int, 0)`. The nil/empty inconsistency is an AI signal \u2014 real code picks one form and sticks with it.",
57213
+ // v0.19.0 — Duplication detector
57214
+ "dup/identical-block": "Refactor to a shared helper. This is a Type-1 clone (byte-for-byte identical after normalization). Common in AI-generated code that copy-pastes from training data."
55062
57215
  };
55063
57216
 
55064
57217
  // src/cli/commands/explain.ts