slopbrick 0.34.2 → 0.34.6

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.
@@ -34436,7 +34436,8 @@ var importPathMismatchRule = createRule({
34436
34436
 
34437
34437
  // src/rules/cpp/c-style-cast.ts
34438
34438
  var C_STYLE_CAST_REGEX = /\(\s*(?:int|long|short|char|float|double|bool|unsigned\s+\w+|signed\s+\w+|size_t|[A-Za-z_]\w*(?:\s*[*,&][^)]*)?)\s*\)\s*(\w+|[^a-zA-Z_])/g;
34439
- var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*\($/;
34439
+ var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*$/;
34440
+ var VOID_CAST_REGEX = /^\s*void\s*$/;
34440
34441
  var cppCStyleCastRule = createRule({
34441
34442
  id: "cpp/c-style-cast",
34442
34443
  category: "typo",
@@ -34456,9 +34457,10 @@ var cppCStyleCastRule = createRule({
34456
34457
  while ((m = C_STYLE_CAST_REGEX.exec(source)) !== null) {
34457
34458
  const innerMatch = /\(\s*([^)]+?)\s*\)/.exec(m[0]) ?? [];
34458
34459
  const inner = (innerMatch[1] ?? "").trim();
34460
+ if (VOID_CAST_REGEX.test(inner)) continue;
34459
34461
  const looksLikeCast = /\b(?:int|long|short|char|float|double|bool|unsigned|signed|size_t)\b/.test(inner) || /[*&]/.test(inner);
34460
34462
  if (!looksLikeCast) continue;
34461
- const before = source.slice(Math.max(0, m.index - 40), m.index);
34463
+ const before = source.slice(Math.max(0, m.index - 60), m.index);
34462
34464
  if (NAMED_CAST_PREFIX_REGEX.test(before)) continue;
34463
34465
  const line = source.slice(0, m.index).split("\n").length;
34464
34466
  issues.push({
@@ -34469,7 +34471,7 @@ var cppCStyleCastRule = createRule({
34469
34471
  message: `C-style cast at line ${line} \u2014 use static_cast / reinterpret_cast / const_cast`,
34470
34472
  line,
34471
34473
  column: 1,
34472
- advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.24."
34474
+ advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.34.3 (refined regex selectivity)."
34473
34475
  });
34474
34476
  }
34475
34477
  return issues;
@@ -34495,6 +34497,7 @@ var cppMagicNumbersRule = createRule({
34495
34497
  if (!/\.(cpp|cc|cxx|h|hpp|hh|hxx|H)$/i.test(facts.filePath)) return issues;
34496
34498
  const lines = source.split("\n");
34497
34499
  const allowSet = /* @__PURE__ */ new Set([
34500
+ // v0.24 originals
34498
34501
  "1024",
34499
34502
  "65535",
34500
34503
  "65536",
@@ -34513,17 +34516,42 @@ var cppMagicNumbersRule = createRule({
34513
34516
  "2",
34514
34517
  "3",
34515
34518
  "4",
34516
- "5"
34519
+ "5",
34520
+ // v0.34.4 additions
34521
+ "-1",
34522
+ // sentinel value for "not found" / "all bits set"
34523
+ "100",
34524
+ // percent literal, very common
34525
+ "0.0",
34526
+ "0.5",
34527
+ "1.0",
34528
+ "2.0",
34529
+ // common probability / ratio
34530
+ "4096",
34531
+ // page size, hash bucket count
34532
+ "2048",
34533
+ "512",
34534
+ // power-of-2 sizes
34535
+ "32",
34536
+ "64",
34537
+ "128",
34538
+ // bit widths, byte sizes
34539
+ "16",
34540
+ "8",
34541
+ // common small constants
34542
+ "50"
34543
+ // percentile literal
34517
34544
  ]);
34518
34545
  for (let i = 0; i < lines.length; i++) {
34519
34546
  const line = lines[i] ?? "";
34520
34547
  if (!COMPARE_OR_RETURN_REGEX.test(line)) continue;
34548
+ const codeLine = line.replace(/\/\/.*$/, "").replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
34521
34549
  let m;
34522
34550
  MAGIC_NUMBER_REGEX.lastIndex = 0;
34523
- while ((m = MAGIC_NUMBER_REGEX.exec(line)) !== null) {
34551
+ while ((m = MAGIC_NUMBER_REGEX.exec(codeLine)) !== null) {
34524
34552
  const literal = m[0] ?? "";
34525
34553
  if (allowSet.has(literal)) continue;
34526
- if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(line.slice(0, m.index).trimEnd())) continue;
34554
+ if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(codeLine.slice(0, m.index).trimEnd())) continue;
34527
34555
  const prevLine = i > 0 ? lines[i - 1] ?? "" : "";
34528
34556
  if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(prevLine.trim())) continue;
34529
34557
  issues.push({
@@ -34534,7 +34562,7 @@ var cppMagicNumbersRule = createRule({
34534
34562
  message: `magic number ${literal} at line ${i + 1} \u2014 name it: constexpr int MAX = ${literal};`,
34535
34563
  line: i + 1,
34536
34564
  column: 1,
34537
- advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.24.'
34565
+ advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.34.4 (expanded allowSet + string/comment exclusion).'
34538
34566
  });
34539
34567
  }
34540
34568
  }
@@ -37187,7 +37215,6 @@ var javaSystemOutPrintlnRule = createRule({
37187
37215
  });
37188
37216
 
37189
37217
  // src/rules/java/thread-sleep-in-loop.ts
37190
- var THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
37191
37218
  var javaThreadSleepInLoopRule = createRule({
37192
37219
  id: "java/thread-sleep-in-loop",
37193
37220
  category: "perf",
@@ -37203,11 +37230,104 @@ var javaThreadSleepInLoopRule = createRule({
37203
37230
  if (!source) return issues;
37204
37231
  if (!/\.java$/i.test(facts.filePath)) return issues;
37205
37232
  if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
37206
- if (!/\b(?:for|while|do)\b/.test(source)) return issues;
37207
- let m;
37208
- THREAD_SLEEP_REGEX.lastIndex = 0;
37209
- while ((m = THREAD_SLEEP_REGEX.exec(source)) !== null) {
37210
- const line = source.slice(0, m.index).split("\n").length;
37233
+ const braceStack = [];
37234
+ const loopSet = /* @__PURE__ */ new Set();
37235
+ let pendingLoopKeyword = null;
37236
+ let parenDepth = 0;
37237
+ const sleepEvents = [];
37238
+ let inString = false;
37239
+ let inLineComment = false;
37240
+ let inBlockComment = false;
37241
+ for (let i = 0; i < source.length; i++) {
37242
+ const c = source[i] ?? "";
37243
+ const next = source[i + 1] ?? "";
37244
+ const prev = source[i - 1] ?? "";
37245
+ if (inLineComment) {
37246
+ if (c === "\n") inLineComment = false;
37247
+ continue;
37248
+ }
37249
+ if (inBlockComment) {
37250
+ if (c === "*" && next === "/") {
37251
+ inBlockComment = false;
37252
+ i++;
37253
+ }
37254
+ continue;
37255
+ }
37256
+ if (inString) {
37257
+ if (c === "\\") {
37258
+ i++;
37259
+ continue;
37260
+ }
37261
+ if (c === inString) inString = false;
37262
+ continue;
37263
+ }
37264
+ if (c === "/" && next === "/") {
37265
+ inLineComment = true;
37266
+ i++;
37267
+ continue;
37268
+ }
37269
+ if (c === "/" && next === "*") {
37270
+ inBlockComment = true;
37271
+ i++;
37272
+ continue;
37273
+ }
37274
+ if (c === '"' || c === "'") {
37275
+ inString = c;
37276
+ continue;
37277
+ }
37278
+ if (c === "(") {
37279
+ parenDepth++;
37280
+ continue;
37281
+ }
37282
+ if (c === ")") {
37283
+ parenDepth = Math.max(0, parenDepth - 1);
37284
+ continue;
37285
+ }
37286
+ if (/[A-Za-z_]/.test(c) && !/[A-Za-z0-9_]/.test(prev)) {
37287
+ const next3 = source.slice(i, i + 3);
37288
+ const next5 = source.slice(i, i + 5);
37289
+ const next2 = source.slice(i, i + 2);
37290
+ const after3 = source[i + 3] ?? "";
37291
+ const after5 = source[i + 5] ?? "";
37292
+ const after2 = source[i + 2] ?? "";
37293
+ if (next3 === "for" && !/[A-Za-z0-9_]/.test(after3)) {
37294
+ pendingLoopKeyword = { kind: "for", idx: i };
37295
+ i += 2;
37296
+ continue;
37297
+ }
37298
+ if (next5 === "while" && !/[A-Za-z0-9_]/.test(after5)) {
37299
+ pendingLoopKeyword = { kind: "while", idx: i };
37300
+ i += 4;
37301
+ continue;
37302
+ }
37303
+ if (next2 === "do" && !/[A-Za-z0-9_]/.test(after2)) {
37304
+ pendingLoopKeyword = { kind: "do", idx: i };
37305
+ i += 1;
37306
+ continue;
37307
+ }
37308
+ }
37309
+ if (c === "{") {
37310
+ braceStack.push(i);
37311
+ if (pendingLoopKeyword && parenDepth === 0) {
37312
+ loopSet.add(i);
37313
+ pendingLoopKeyword = null;
37314
+ }
37315
+ } else if (c === "}") {
37316
+ const popped = braceStack.pop();
37317
+ if (popped !== void 0 && loopSet.has(popped)) {
37318
+ loopSet.delete(popped);
37319
+ }
37320
+ if (pendingLoopKeyword) {
37321
+ pendingLoopKeyword = null;
37322
+ }
37323
+ } else if (c === "T" && source.slice(i, i + 13) === "Thread.sleep(") {
37324
+ sleepEvents.push({ idx: i, loopDepth: loopSet.size });
37325
+ i += 12;
37326
+ }
37327
+ }
37328
+ for (const ev of sleepEvents) {
37329
+ if (ev.loopDepth === 0) continue;
37330
+ const line = source.slice(0, ev.idx).split("\n").length;
37211
37331
  issues.push({
37212
37332
  ruleId: "java/thread-sleep-in-loop",
37213
37333
  category: "perf",
@@ -37216,7 +37336,7 @@ var javaThreadSleepInLoopRule = createRule({
37216
37336
  message: `Thread.sleep() at line ${line}`,
37217
37337
  line,
37218
37338
  column: 1,
37219
- advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.30.'
37339
+ advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.34.6 (refined to require Thread.sleep inside the loop block, not just in the file).'
37220
37340
  });
37221
37341
  }
37222
37342
  return issues;
@@ -37463,6 +37583,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
37463
37583
  // src/rules/kotlin/println-as-log.ts
37464
37584
  var PRINTLN_REGEX = /\bprintln\s*\(/g;
37465
37585
  var REAL_LOGGING_IMPORT_REGEX2 = /\bimport\s+(?:android\.util\.Log|org\.slf4j\.|io\.github\.oshai\.kotlinlogging|kotlin\.logging|co\.touchlab\.kermit|com\.github\.ajalt\.timber|org\.apache\.logging\.log4j)/;
37586
+ var TEST_FILE_REGEX = /(?:\/src\/test\/|\/test\/|\/Tests\/|\/Test\.kt|\/Tests\.kt|Tests\.kt$|Test\.kt$)/;
37466
37587
  var kotlinPrintlnAsLogRule = createRule({
37467
37588
  id: "kotlin/println-as-log",
37468
37589
  category: "logic",
@@ -37477,7 +37598,7 @@ var kotlinPrintlnAsLogRule = createRule({
37477
37598
  const source = facts.v2?._source;
37478
37599
  if (!source) return issues;
37479
37600
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
37480
- if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) return issues;
37601
+ if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
37481
37602
  if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
37482
37603
  let m;
37483
37604
  PRINTLN_REGEX.lastIndex = 0;
@@ -37491,7 +37612,7 @@ var kotlinPrintlnAsLogRule = createRule({
37491
37612
  message: `println() as logger at line ${line}`,
37492
37613
  line,
37493
37614
  column: 1,
37494
- advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.29."
37615
+ advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.34.5 (refined to skip test files for higher precision)."
37495
37616
  });
37496
37617
  }
37497
37618
  return issues;
@@ -40514,7 +40635,7 @@ var swiftImplicitlyUnwrappedOptionalRule = createRule({
40514
40635
  // src/rules/swift/print-debug.ts
40515
40636
  var PRINT_REGEX = /\bprint\s*\(/g;
40516
40637
  var DEFAULT_THRESHOLD2 = 1;
40517
- var TEST_FILE_REGEX = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
40638
+ var TEST_FILE_REGEX2 = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
40518
40639
  var swiftPrintDebugRule = createRule({
40519
40640
  id: "swift/print-debug",
40520
40641
  category: "typo",
@@ -40529,7 +40650,7 @@ var swiftPrintDebugRule = createRule({
40529
40650
  const source = facts.v2?._source;
40530
40651
  if (!source) return issues;
40531
40652
  if (!/\.swift$/i.test(facts.filePath)) return issues;
40532
- if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
40653
+ if (TEST_FILE_REGEX2.test(facts.filePath)) return issues;
40533
40654
  const matches = [];
40534
40655
  let m;
40535
40656
  PRINT_REGEX.lastIndex = 0;
@@ -45465,7 +45586,7 @@ var signal_strength_default = {
45465
45586
  precision: 0.1268,
45466
45587
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45467
45588
  verdict: "OK",
45468
- _calibrationNote: "v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
45589
+ _calibrationNote: "v0.34.5: REFINED \u2014 rule now skips test files (JUnit4/5 + Android: *Tests.kt, *Test.kt, src/test/, test/ dirs). Per-file unique v9 Kotlin calibration (2698 neg, 213 pos): ratio=1.84 (\u22651.5, OK). The refinement mirrors v0.34.2's swift/print-debug fix: the previous exclusion only matched `\\/test\\/` and `.test.kts?$`, missing JUnit5's `FooTests.kt` naming. Expected post-refinement precision: 12.7% \u2192 25%+ by removing the JUnit assertion / debug-output fires that were ~50% of the FPs. Same direction as java/system-out-println (1.73 refined) and the v0.34.2 swift/print-debug pipeline. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
45469
45590
  aiSpecific: false,
45470
45591
  _v9Verdict: "OK",
45471
45592
  _v9Lift: 1.84,
@@ -45529,7 +45650,7 @@ var signal_strength_default = {
45529
45650
  precision: 0.1091,
45530
45651
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45531
45652
  verdict: "DORMANT",
45532
- _calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
45653
+ _calibrationNote: "v0.34.6: REFINED \u2014 rule now requires Thread.sleep to be INSIDE the loop block (verified via brace-counting), not just in a file that happens to contain a for/while/do keyword. v0.30 baseline: 2154 total fires, ratio 0.97 (DORMANT). The refinement targets: (1) Thread.sleep in `main()` no longer fires if a different method has a `for` loop (the v0.30 heuristic fired on every Thread.sleep in the file); (2) Thread.sleep before/after a loop block (not inside it) no longer fires; (3) string literals containing `Thread.sleep(...)` are skipped via the string-state-machine. Expected post-refinement ratio: 1.5+ (positive-signal OK verdict) by removing the cross-method over-fires. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
45533
45654
  aiSpecific: false,
45534
45655
  _v9Verdict: "DORMANT",
45535
45656
  _v9Lift: 0.97,
@@ -45738,7 +45859,7 @@ var signal_strength_default = {
45738
45859
  precision: 0.2325,
45739
45860
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45740
45861
  verdict: "DORMANT",
45741
- _calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
45862
+ _calibrationNote: "v0.34.3: REFINED \u2014 NAMED_CAST_PREFIX_REGEX tightened (the old version required the lookback slice to end with `>(`, which never matched because the slice ends with `>`). Lookback increased 40\u219260 chars. Added `(void)x` discard-idom exclusion. v0.33 baseline: 874 TP / 2885 FP per-file (ratio 0.93, DORMANT). The refinement targets: (1) static_cast/const_cast/reinterpret_cast/dynamic_cast no longer fire when followed by whitespace before `(`; (2) class-type named casts like `static_cast<MyClass*>(p)` now correctly excluded (60-char lookback reaches the long typename); (3) `(void)x` deliberate discards are no longer flagged. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
45742
45863
  aiSpecific: true,
45743
45864
  _v7Verdict: "DORMANT",
45744
45865
  _v7Lift: 1,
@@ -45784,7 +45905,7 @@ var signal_strength_default = {
45784
45905
  precision: 0.2187,
45785
45906
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45786
45907
  verdict: "DORMANT",
45787
- _calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
45908
+ _calibrationNote: "v0.34.4: REFINED \u2014 expanded allowSet with common constants (-1 sentinel, 100 percent literal, 0.5/1.0/2.0 ratios, 4096/2048/512/128/64/32/16/8 power-of-2 sizes, 50 percentile). Added string-literal and `//` comment stripping so substrings like `\"got 42 errors\"` and `// ticket #4242` no longer fire. v0.33 baseline: 220 TP / 786 FP per-file (ratio 0.86, DORMANT). The refinement targets: (1) sentinel `-1` literals are skipped because `1` is allowlisted and the `-` doesn't match MAGIC_NUMBER_REGEX anyway; (2) hex literals (`0xFF`) are skipped because the digit-only regex requires a word-boundary after digits, and `0x...` has none; (3) literals inside string/comment contexts no longer fire. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
45788
45909
  aiSpecific: true,
45789
45910
  _v7Verdict: "DORMANT",
45790
45911
  _v7Lift: 1,
@@ -34407,7 +34407,8 @@ var importPathMismatchRule = createRule({
34407
34407
 
34408
34408
  // src/rules/cpp/c-style-cast.ts
34409
34409
  var C_STYLE_CAST_REGEX = /\(\s*(?:int|long|short|char|float|double|bool|unsigned\s+\w+|signed\s+\w+|size_t|[A-Za-z_]\w*(?:\s*[*,&][^)]*)?)\s*\)\s*(\w+|[^a-zA-Z_])/g;
34410
- var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*\($/;
34410
+ var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*$/;
34411
+ var VOID_CAST_REGEX = /^\s*void\s*$/;
34411
34412
  var cppCStyleCastRule = createRule({
34412
34413
  id: "cpp/c-style-cast",
34413
34414
  category: "typo",
@@ -34427,9 +34428,10 @@ var cppCStyleCastRule = createRule({
34427
34428
  while ((m = C_STYLE_CAST_REGEX.exec(source)) !== null) {
34428
34429
  const innerMatch = /\(\s*([^)]+?)\s*\)/.exec(m[0]) ?? [];
34429
34430
  const inner = (innerMatch[1] ?? "").trim();
34431
+ if (VOID_CAST_REGEX.test(inner)) continue;
34430
34432
  const looksLikeCast = /\b(?:int|long|short|char|float|double|bool|unsigned|signed|size_t)\b/.test(inner) || /[*&]/.test(inner);
34431
34433
  if (!looksLikeCast) continue;
34432
- const before = source.slice(Math.max(0, m.index - 40), m.index);
34434
+ const before = source.slice(Math.max(0, m.index - 60), m.index);
34433
34435
  if (NAMED_CAST_PREFIX_REGEX.test(before)) continue;
34434
34436
  const line = source.slice(0, m.index).split("\n").length;
34435
34437
  issues.push({
@@ -34440,7 +34442,7 @@ var cppCStyleCastRule = createRule({
34440
34442
  message: `C-style cast at line ${line} \u2014 use static_cast / reinterpret_cast / const_cast`,
34441
34443
  line,
34442
34444
  column: 1,
34443
- advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.24."
34445
+ advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.34.3 (refined regex selectivity)."
34444
34446
  });
34445
34447
  }
34446
34448
  return issues;
@@ -34466,6 +34468,7 @@ var cppMagicNumbersRule = createRule({
34466
34468
  if (!/\.(cpp|cc|cxx|h|hpp|hh|hxx|H)$/i.test(facts.filePath)) return issues;
34467
34469
  const lines = source.split("\n");
34468
34470
  const allowSet = /* @__PURE__ */ new Set([
34471
+ // v0.24 originals
34469
34472
  "1024",
34470
34473
  "65535",
34471
34474
  "65536",
@@ -34484,17 +34487,42 @@ var cppMagicNumbersRule = createRule({
34484
34487
  "2",
34485
34488
  "3",
34486
34489
  "4",
34487
- "5"
34490
+ "5",
34491
+ // v0.34.4 additions
34492
+ "-1",
34493
+ // sentinel value for "not found" / "all bits set"
34494
+ "100",
34495
+ // percent literal, very common
34496
+ "0.0",
34497
+ "0.5",
34498
+ "1.0",
34499
+ "2.0",
34500
+ // common probability / ratio
34501
+ "4096",
34502
+ // page size, hash bucket count
34503
+ "2048",
34504
+ "512",
34505
+ // power-of-2 sizes
34506
+ "32",
34507
+ "64",
34508
+ "128",
34509
+ // bit widths, byte sizes
34510
+ "16",
34511
+ "8",
34512
+ // common small constants
34513
+ "50"
34514
+ // percentile literal
34488
34515
  ]);
34489
34516
  for (let i = 0; i < lines.length; i++) {
34490
34517
  const line = lines[i] ?? "";
34491
34518
  if (!COMPARE_OR_RETURN_REGEX.test(line)) continue;
34519
+ const codeLine = line.replace(/\/\/.*$/, "").replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
34492
34520
  let m;
34493
34521
  MAGIC_NUMBER_REGEX.lastIndex = 0;
34494
- while ((m = MAGIC_NUMBER_REGEX.exec(line)) !== null) {
34522
+ while ((m = MAGIC_NUMBER_REGEX.exec(codeLine)) !== null) {
34495
34523
  const literal = m[0] ?? "";
34496
34524
  if (allowSet.has(literal)) continue;
34497
- if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(line.slice(0, m.index).trimEnd())) continue;
34525
+ if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(codeLine.slice(0, m.index).trimEnd())) continue;
34498
34526
  const prevLine = i > 0 ? lines[i - 1] ?? "" : "";
34499
34527
  if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(prevLine.trim())) continue;
34500
34528
  issues.push({
@@ -34505,7 +34533,7 @@ var cppMagicNumbersRule = createRule({
34505
34533
  message: `magic number ${literal} at line ${i + 1} \u2014 name it: constexpr int MAX = ${literal};`,
34506
34534
  line: i + 1,
34507
34535
  column: 1,
34508
- advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.24.'
34536
+ advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.34.4 (expanded allowSet + string/comment exclusion).'
34509
34537
  });
34510
34538
  }
34511
34539
  }
@@ -37158,7 +37186,6 @@ var javaSystemOutPrintlnRule = createRule({
37158
37186
  });
37159
37187
 
37160
37188
  // src/rules/java/thread-sleep-in-loop.ts
37161
- var THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
37162
37189
  var javaThreadSleepInLoopRule = createRule({
37163
37190
  id: "java/thread-sleep-in-loop",
37164
37191
  category: "perf",
@@ -37174,11 +37201,104 @@ var javaThreadSleepInLoopRule = createRule({
37174
37201
  if (!source) return issues;
37175
37202
  if (!/\.java$/i.test(facts.filePath)) return issues;
37176
37203
  if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
37177
- if (!/\b(?:for|while|do)\b/.test(source)) return issues;
37178
- let m;
37179
- THREAD_SLEEP_REGEX.lastIndex = 0;
37180
- while ((m = THREAD_SLEEP_REGEX.exec(source)) !== null) {
37181
- const line = source.slice(0, m.index).split("\n").length;
37204
+ const braceStack = [];
37205
+ const loopSet = /* @__PURE__ */ new Set();
37206
+ let pendingLoopKeyword = null;
37207
+ let parenDepth = 0;
37208
+ const sleepEvents = [];
37209
+ let inString = false;
37210
+ let inLineComment = false;
37211
+ let inBlockComment = false;
37212
+ for (let i = 0; i < source.length; i++) {
37213
+ const c = source[i] ?? "";
37214
+ const next = source[i + 1] ?? "";
37215
+ const prev = source[i - 1] ?? "";
37216
+ if (inLineComment) {
37217
+ if (c === "\n") inLineComment = false;
37218
+ continue;
37219
+ }
37220
+ if (inBlockComment) {
37221
+ if (c === "*" && next === "/") {
37222
+ inBlockComment = false;
37223
+ i++;
37224
+ }
37225
+ continue;
37226
+ }
37227
+ if (inString) {
37228
+ if (c === "\\") {
37229
+ i++;
37230
+ continue;
37231
+ }
37232
+ if (c === inString) inString = false;
37233
+ continue;
37234
+ }
37235
+ if (c === "/" && next === "/") {
37236
+ inLineComment = true;
37237
+ i++;
37238
+ continue;
37239
+ }
37240
+ if (c === "/" && next === "*") {
37241
+ inBlockComment = true;
37242
+ i++;
37243
+ continue;
37244
+ }
37245
+ if (c === '"' || c === "'") {
37246
+ inString = c;
37247
+ continue;
37248
+ }
37249
+ if (c === "(") {
37250
+ parenDepth++;
37251
+ continue;
37252
+ }
37253
+ if (c === ")") {
37254
+ parenDepth = Math.max(0, parenDepth - 1);
37255
+ continue;
37256
+ }
37257
+ if (/[A-Za-z_]/.test(c) && !/[A-Za-z0-9_]/.test(prev)) {
37258
+ const next3 = source.slice(i, i + 3);
37259
+ const next5 = source.slice(i, i + 5);
37260
+ const next2 = source.slice(i, i + 2);
37261
+ const after3 = source[i + 3] ?? "";
37262
+ const after5 = source[i + 5] ?? "";
37263
+ const after2 = source[i + 2] ?? "";
37264
+ if (next3 === "for" && !/[A-Za-z0-9_]/.test(after3)) {
37265
+ pendingLoopKeyword = { kind: "for", idx: i };
37266
+ i += 2;
37267
+ continue;
37268
+ }
37269
+ if (next5 === "while" && !/[A-Za-z0-9_]/.test(after5)) {
37270
+ pendingLoopKeyword = { kind: "while", idx: i };
37271
+ i += 4;
37272
+ continue;
37273
+ }
37274
+ if (next2 === "do" && !/[A-Za-z0-9_]/.test(after2)) {
37275
+ pendingLoopKeyword = { kind: "do", idx: i };
37276
+ i += 1;
37277
+ continue;
37278
+ }
37279
+ }
37280
+ if (c === "{") {
37281
+ braceStack.push(i);
37282
+ if (pendingLoopKeyword && parenDepth === 0) {
37283
+ loopSet.add(i);
37284
+ pendingLoopKeyword = null;
37285
+ }
37286
+ } else if (c === "}") {
37287
+ const popped = braceStack.pop();
37288
+ if (popped !== void 0 && loopSet.has(popped)) {
37289
+ loopSet.delete(popped);
37290
+ }
37291
+ if (pendingLoopKeyword) {
37292
+ pendingLoopKeyword = null;
37293
+ }
37294
+ } else if (c === "T" && source.slice(i, i + 13) === "Thread.sleep(") {
37295
+ sleepEvents.push({ idx: i, loopDepth: loopSet.size });
37296
+ i += 12;
37297
+ }
37298
+ }
37299
+ for (const ev of sleepEvents) {
37300
+ if (ev.loopDepth === 0) continue;
37301
+ const line = source.slice(0, ev.idx).split("\n").length;
37182
37302
  issues.push({
37183
37303
  ruleId: "java/thread-sleep-in-loop",
37184
37304
  category: "perf",
@@ -37187,7 +37307,7 @@ var javaThreadSleepInLoopRule = createRule({
37187
37307
  message: `Thread.sleep() at line ${line}`,
37188
37308
  line,
37189
37309
  column: 1,
37190
- advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.30.'
37310
+ advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.34.6 (refined to require Thread.sleep inside the loop block, not just in the file).'
37191
37311
  });
37192
37312
  }
37193
37313
  return issues;
@@ -37434,6 +37554,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
37434
37554
  // src/rules/kotlin/println-as-log.ts
37435
37555
  var PRINTLN_REGEX = /\bprintln\s*\(/g;
37436
37556
  var REAL_LOGGING_IMPORT_REGEX2 = /\bimport\s+(?:android\.util\.Log|org\.slf4j\.|io\.github\.oshai\.kotlinlogging|kotlin\.logging|co\.touchlab\.kermit|com\.github\.ajalt\.timber|org\.apache\.logging\.log4j)/;
37557
+ var TEST_FILE_REGEX = /(?:\/src\/test\/|\/test\/|\/Tests\/|\/Test\.kt|\/Tests\.kt|Tests\.kt$|Test\.kt$)/;
37437
37558
  var kotlinPrintlnAsLogRule = createRule({
37438
37559
  id: "kotlin/println-as-log",
37439
37560
  category: "logic",
@@ -37448,7 +37569,7 @@ var kotlinPrintlnAsLogRule = createRule({
37448
37569
  const source = facts.v2?._source;
37449
37570
  if (!source) return issues;
37450
37571
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
37451
- if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) return issues;
37572
+ if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
37452
37573
  if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
37453
37574
  let m;
37454
37575
  PRINTLN_REGEX.lastIndex = 0;
@@ -37462,7 +37583,7 @@ var kotlinPrintlnAsLogRule = createRule({
37462
37583
  message: `println() as logger at line ${line}`,
37463
37584
  line,
37464
37585
  column: 1,
37465
- advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.29."
37586
+ advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.34.5 (refined to skip test files for higher precision)."
37466
37587
  });
37467
37588
  }
37468
37589
  return issues;
@@ -40485,7 +40606,7 @@ var swiftImplicitlyUnwrappedOptionalRule = createRule({
40485
40606
  // src/rules/swift/print-debug.ts
40486
40607
  var PRINT_REGEX = /\bprint\s*\(/g;
40487
40608
  var DEFAULT_THRESHOLD2 = 1;
40488
- var TEST_FILE_REGEX = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
40609
+ var TEST_FILE_REGEX2 = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
40489
40610
  var swiftPrintDebugRule = createRule({
40490
40611
  id: "swift/print-debug",
40491
40612
  category: "typo",
@@ -40500,7 +40621,7 @@ var swiftPrintDebugRule = createRule({
40500
40621
  const source = facts.v2?._source;
40501
40622
  if (!source) return issues;
40502
40623
  if (!/\.swift$/i.test(facts.filePath)) return issues;
40503
- if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
40624
+ if (TEST_FILE_REGEX2.test(facts.filePath)) return issues;
40504
40625
  const matches = [];
40505
40626
  let m;
40506
40627
  PRINT_REGEX.lastIndex = 0;
@@ -45436,7 +45557,7 @@ var signal_strength_default = {
45436
45557
  precision: 0.1268,
45437
45558
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45438
45559
  verdict: "OK",
45439
- _calibrationNote: "v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
45560
+ _calibrationNote: "v0.34.5: REFINED \u2014 rule now skips test files (JUnit4/5 + Android: *Tests.kt, *Test.kt, src/test/, test/ dirs). Per-file unique v9 Kotlin calibration (2698 neg, 213 pos): ratio=1.84 (\u22651.5, OK). The refinement mirrors v0.34.2's swift/print-debug fix: the previous exclusion only matched `\\/test\\/` and `.test.kts?$`, missing JUnit5's `FooTests.kt` naming. Expected post-refinement precision: 12.7% \u2192 25%+ by removing the JUnit assertion / debug-output fires that were ~50% of the FPs. Same direction as java/system-out-println (1.73 refined) and the v0.34.2 swift/print-debug pipeline. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
45440
45561
  aiSpecific: false,
45441
45562
  _v9Verdict: "OK",
45442
45563
  _v9Lift: 1.84,
@@ -45500,7 +45621,7 @@ var signal_strength_default = {
45500
45621
  precision: 0.1091,
45501
45622
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45502
45623
  verdict: "DORMANT",
45503
- _calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
45624
+ _calibrationNote: "v0.34.6: REFINED \u2014 rule now requires Thread.sleep to be INSIDE the loop block (verified via brace-counting), not just in a file that happens to contain a for/while/do keyword. v0.30 baseline: 2154 total fires, ratio 0.97 (DORMANT). The refinement targets: (1) Thread.sleep in `main()` no longer fires if a different method has a `for` loop (the v0.30 heuristic fired on every Thread.sleep in the file); (2) Thread.sleep before/after a loop block (not inside it) no longer fires; (3) string literals containing `Thread.sleep(...)` are skipped via the string-state-machine. Expected post-refinement ratio: 1.5+ (positive-signal OK verdict) by removing the cross-method over-fires. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
45504
45625
  aiSpecific: false,
45505
45626
  _v9Verdict: "DORMANT",
45506
45627
  _v9Lift: 0.97,
@@ -45709,7 +45830,7 @@ var signal_strength_default = {
45709
45830
  precision: 0.2325,
45710
45831
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45711
45832
  verdict: "DORMANT",
45712
- _calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
45833
+ _calibrationNote: "v0.34.3: REFINED \u2014 NAMED_CAST_PREFIX_REGEX tightened (the old version required the lookback slice to end with `>(`, which never matched because the slice ends with `>`). Lookback increased 40\u219260 chars. Added `(void)x` discard-idom exclusion. v0.33 baseline: 874 TP / 2885 FP per-file (ratio 0.93, DORMANT). The refinement targets: (1) static_cast/const_cast/reinterpret_cast/dynamic_cast no longer fire when followed by whitespace before `(`; (2) class-type named casts like `static_cast<MyClass*>(p)` now correctly excluded (60-char lookback reaches the long typename); (3) `(void)x` deliberate discards are no longer flagged. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
45713
45834
  aiSpecific: true,
45714
45835
  _v7Verdict: "DORMANT",
45715
45836
  _v7Lift: 1,
@@ -45755,7 +45876,7 @@ var signal_strength_default = {
45755
45876
  precision: 0.2187,
45756
45877
  lastCalibratedAt: "2026-07-03T00:00:00Z",
45757
45878
  verdict: "DORMANT",
45758
- _calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
45879
+ _calibrationNote: "v0.34.4: REFINED \u2014 expanded allowSet with common constants (-1 sentinel, 100 percent literal, 0.5/1.0/2.0 ratios, 4096/2048/512/128/64/32/16/8 power-of-2 sizes, 50 percentile). Added string-literal and `//` comment stripping so substrings like `\"got 42 errors\"` and `// ticket #4242` no longer fire. v0.33 baseline: 220 TP / 786 FP per-file (ratio 0.86, DORMANT). The refinement targets: (1) sentinel `-1` literals are skipped because `1` is allowlisted and the `-` doesn't match MAGIC_NUMBER_REGEX anyway; (2) hex literals (`0xFF`) are skipped because the digit-only regex requires a word-boundary after digits, and `0x...` has none; (3) literals inside string/comment contexts no longer fire. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
45759
45880
  aiSpecific: true,
45760
45881
  _v7Verdict: "DORMANT",
45761
45882
  _v7Lift: 1,