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.
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.34.2";
22
+ VERSION = "0.34.6";
23
23
  }
24
24
  });
25
25
 
@@ -27642,13 +27642,14 @@ var init_import_path_mismatch = __esm({
27642
27642
  });
27643
27643
 
27644
27644
  // src/rules/cpp/c-style-cast.ts
27645
- var C_STYLE_CAST_REGEX, NAMED_CAST_PREFIX_REGEX, cppCStyleCastRule;
27645
+ var C_STYLE_CAST_REGEX, NAMED_CAST_PREFIX_REGEX, VOID_CAST_REGEX, cppCStyleCastRule;
27646
27646
  var init_c_style_cast = __esm({
27647
27647
  "src/rules/cpp/c-style-cast.ts"() {
27648
27648
  "use strict";
27649
27649
  init_rule();
27650
27650
  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;
27651
- NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*\($/;
27651
+ NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*$/;
27652
+ VOID_CAST_REGEX = /^\s*void\s*$/;
27652
27653
  cppCStyleCastRule = createRule({
27653
27654
  id: "cpp/c-style-cast",
27654
27655
  category: "typo",
@@ -27668,9 +27669,10 @@ var init_c_style_cast = __esm({
27668
27669
  while ((m = C_STYLE_CAST_REGEX.exec(source)) !== null) {
27669
27670
  const innerMatch = /\(\s*([^)]+?)\s*\)/.exec(m[0]) ?? [];
27670
27671
  const inner = (innerMatch[1] ?? "").trim();
27672
+ if (VOID_CAST_REGEX.test(inner)) continue;
27671
27673
  const looksLikeCast = /\b(?:int|long|short|char|float|double|bool|unsigned|signed|size_t)\b/.test(inner) || /[*&]/.test(inner);
27672
27674
  if (!looksLikeCast) continue;
27673
- const before = source.slice(Math.max(0, m.index - 40), m.index);
27675
+ const before = source.slice(Math.max(0, m.index - 60), m.index);
27674
27676
  if (NAMED_CAST_PREFIX_REGEX.test(before)) continue;
27675
27677
  const line = source.slice(0, m.index).split("\n").length;
27676
27678
  issues.push({
@@ -27681,7 +27683,7 @@ var init_c_style_cast = __esm({
27681
27683
  message: `C-style cast at line ${line} \u2014 use static_cast / reinterpret_cast / const_cast`,
27682
27684
  line,
27683
27685
  column: 1,
27684
- 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."
27686
+ 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)."
27685
27687
  });
27686
27688
  }
27687
27689
  return issues;
@@ -27714,6 +27716,7 @@ var init_magic_numbers = __esm({
27714
27716
  if (!/\.(cpp|cc|cxx|h|hpp|hh|hxx|H)$/i.test(facts.filePath)) return issues;
27715
27717
  const lines = source.split("\n");
27716
27718
  const allowSet = /* @__PURE__ */ new Set([
27719
+ // v0.24 originals
27717
27720
  "1024",
27718
27721
  "65535",
27719
27722
  "65536",
@@ -27732,17 +27735,42 @@ var init_magic_numbers = __esm({
27732
27735
  "2",
27733
27736
  "3",
27734
27737
  "4",
27735
- "5"
27738
+ "5",
27739
+ // v0.34.4 additions
27740
+ "-1",
27741
+ // sentinel value for "not found" / "all bits set"
27742
+ "100",
27743
+ // percent literal, very common
27744
+ "0.0",
27745
+ "0.5",
27746
+ "1.0",
27747
+ "2.0",
27748
+ // common probability / ratio
27749
+ "4096",
27750
+ // page size, hash bucket count
27751
+ "2048",
27752
+ "512",
27753
+ // power-of-2 sizes
27754
+ "32",
27755
+ "64",
27756
+ "128",
27757
+ // bit widths, byte sizes
27758
+ "16",
27759
+ "8",
27760
+ // common small constants
27761
+ "50"
27762
+ // percentile literal
27736
27763
  ]);
27737
27764
  for (let i = 0; i < lines.length; i++) {
27738
27765
  const line = lines[i] ?? "";
27739
27766
  if (!COMPARE_OR_RETURN_REGEX.test(line)) continue;
27767
+ const codeLine = line.replace(/\/\/.*$/, "").replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
27740
27768
  let m;
27741
27769
  MAGIC_NUMBER_REGEX.lastIndex = 0;
27742
- while ((m = MAGIC_NUMBER_REGEX.exec(line)) !== null) {
27770
+ while ((m = MAGIC_NUMBER_REGEX.exec(codeLine)) !== null) {
27743
27771
  const literal = m[0] ?? "";
27744
27772
  if (allowSet.has(literal)) continue;
27745
- if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(line.slice(0, m.index).trimEnd())) continue;
27773
+ if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(codeLine.slice(0, m.index).trimEnd())) continue;
27746
27774
  const prevLine = i > 0 ? lines[i - 1] ?? "" : "";
27747
27775
  if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(prevLine.trim())) continue;
27748
27776
  issues.push({
@@ -27753,7 +27781,7 @@ var init_magic_numbers = __esm({
27753
27781
  message: `magic number ${literal} at line ${i + 1} \u2014 name it: constexpr int MAX = ${literal};`,
27754
27782
  line: i + 1,
27755
27783
  column: 1,
27756
- 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.'
27784
+ 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).'
27757
27785
  });
27758
27786
  }
27759
27787
  }
@@ -30840,12 +30868,11 @@ var init_system_out_println = __esm({
30840
30868
  });
30841
30869
 
30842
30870
  // src/rules/java/thread-sleep-in-loop.ts
30843
- var THREAD_SLEEP_REGEX, javaThreadSleepInLoopRule;
30871
+ var javaThreadSleepInLoopRule;
30844
30872
  var init_thread_sleep_in_loop = __esm({
30845
30873
  "src/rules/java/thread-sleep-in-loop.ts"() {
30846
30874
  "use strict";
30847
30875
  init_rule();
30848
- THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
30849
30876
  javaThreadSleepInLoopRule = createRule({
30850
30877
  id: "java/thread-sleep-in-loop",
30851
30878
  category: "perf",
@@ -30861,11 +30888,104 @@ var init_thread_sleep_in_loop = __esm({
30861
30888
  if (!source) return issues;
30862
30889
  if (!/\.java$/i.test(facts.filePath)) return issues;
30863
30890
  if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
30864
- if (!/\b(?:for|while|do)\b/.test(source)) return issues;
30865
- let m;
30866
- THREAD_SLEEP_REGEX.lastIndex = 0;
30867
- while ((m = THREAD_SLEEP_REGEX.exec(source)) !== null) {
30868
- const line = source.slice(0, m.index).split("\n").length;
30891
+ const braceStack = [];
30892
+ const loopSet = /* @__PURE__ */ new Set();
30893
+ let pendingLoopKeyword = null;
30894
+ let parenDepth = 0;
30895
+ const sleepEvents = [];
30896
+ let inString = false;
30897
+ let inLineComment = false;
30898
+ let inBlockComment = false;
30899
+ for (let i = 0; i < source.length; i++) {
30900
+ const c = source[i] ?? "";
30901
+ const next = source[i + 1] ?? "";
30902
+ const prev = source[i - 1] ?? "";
30903
+ if (inLineComment) {
30904
+ if (c === "\n") inLineComment = false;
30905
+ continue;
30906
+ }
30907
+ if (inBlockComment) {
30908
+ if (c === "*" && next === "/") {
30909
+ inBlockComment = false;
30910
+ i++;
30911
+ }
30912
+ continue;
30913
+ }
30914
+ if (inString) {
30915
+ if (c === "\\") {
30916
+ i++;
30917
+ continue;
30918
+ }
30919
+ if (c === inString) inString = false;
30920
+ continue;
30921
+ }
30922
+ if (c === "/" && next === "/") {
30923
+ inLineComment = true;
30924
+ i++;
30925
+ continue;
30926
+ }
30927
+ if (c === "/" && next === "*") {
30928
+ inBlockComment = true;
30929
+ i++;
30930
+ continue;
30931
+ }
30932
+ if (c === '"' || c === "'") {
30933
+ inString = c;
30934
+ continue;
30935
+ }
30936
+ if (c === "(") {
30937
+ parenDepth++;
30938
+ continue;
30939
+ }
30940
+ if (c === ")") {
30941
+ parenDepth = Math.max(0, parenDepth - 1);
30942
+ continue;
30943
+ }
30944
+ if (/[A-Za-z_]/.test(c) && !/[A-Za-z0-9_]/.test(prev)) {
30945
+ const next3 = source.slice(i, i + 3);
30946
+ const next5 = source.slice(i, i + 5);
30947
+ const next2 = source.slice(i, i + 2);
30948
+ const after3 = source[i + 3] ?? "";
30949
+ const after5 = source[i + 5] ?? "";
30950
+ const after2 = source[i + 2] ?? "";
30951
+ if (next3 === "for" && !/[A-Za-z0-9_]/.test(after3)) {
30952
+ pendingLoopKeyword = { kind: "for", idx: i };
30953
+ i += 2;
30954
+ continue;
30955
+ }
30956
+ if (next5 === "while" && !/[A-Za-z0-9_]/.test(after5)) {
30957
+ pendingLoopKeyword = { kind: "while", idx: i };
30958
+ i += 4;
30959
+ continue;
30960
+ }
30961
+ if (next2 === "do" && !/[A-Za-z0-9_]/.test(after2)) {
30962
+ pendingLoopKeyword = { kind: "do", idx: i };
30963
+ i += 1;
30964
+ continue;
30965
+ }
30966
+ }
30967
+ if (c === "{") {
30968
+ braceStack.push(i);
30969
+ if (pendingLoopKeyword && parenDepth === 0) {
30970
+ loopSet.add(i);
30971
+ pendingLoopKeyword = null;
30972
+ }
30973
+ } else if (c === "}") {
30974
+ const popped = braceStack.pop();
30975
+ if (popped !== void 0 && loopSet.has(popped)) {
30976
+ loopSet.delete(popped);
30977
+ }
30978
+ if (pendingLoopKeyword) {
30979
+ pendingLoopKeyword = null;
30980
+ }
30981
+ } else if (c === "T" && source.slice(i, i + 13) === "Thread.sleep(") {
30982
+ sleepEvents.push({ idx: i, loopDepth: loopSet.size });
30983
+ i += 12;
30984
+ }
30985
+ }
30986
+ for (const ev of sleepEvents) {
30987
+ if (ev.loopDepth === 0) continue;
30988
+ const line = source.slice(0, ev.idx).split("\n").length;
30869
30989
  issues.push({
30870
30990
  ruleId: "java/thread-sleep-in-loop",
30871
30991
  category: "perf",
@@ -30874,7 +30994,7 @@ var init_thread_sleep_in_loop = __esm({
30874
30994
  message: `Thread.sleep() at line ${line}`,
30875
30995
  line,
30876
30996
  column: 1,
30877
- 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.'
30997
+ 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).'
30878
30998
  });
30879
30999
  }
30880
31000
  return issues;
@@ -31156,13 +31276,14 @@ var init_object_singleton_misuse = __esm({
31156
31276
  });
31157
31277
 
31158
31278
  // src/rules/kotlin/println-as-log.ts
31159
- var PRINTLN_REGEX, REAL_LOGGING_IMPORT_REGEX2, kotlinPrintlnAsLogRule;
31279
+ var PRINTLN_REGEX, REAL_LOGGING_IMPORT_REGEX2, TEST_FILE_REGEX, kotlinPrintlnAsLogRule;
31160
31280
  var init_println_as_log = __esm({
31161
31281
  "src/rules/kotlin/println-as-log.ts"() {
31162
31282
  "use strict";
31163
31283
  init_rule();
31164
31284
  PRINTLN_REGEX = /\bprintln\s*\(/g;
31165
31285
  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)/;
31286
+ TEST_FILE_REGEX = /(?:\/src\/test\/|\/test\/|\/Tests\/|\/Test\.kt|\/Tests\.kt|Tests\.kt$|Test\.kt$)/;
31166
31287
  kotlinPrintlnAsLogRule = createRule({
31167
31288
  id: "kotlin/println-as-log",
31168
31289
  category: "logic",
@@ -31177,7 +31298,7 @@ var init_println_as_log = __esm({
31177
31298
  const source = facts.v2?._source;
31178
31299
  if (!source) return issues;
31179
31300
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
31180
- if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) return issues;
31301
+ if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
31181
31302
  if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
31182
31303
  let m;
31183
31304
  PRINTLN_REGEX.lastIndex = 0;
@@ -31191,7 +31312,7 @@ var init_println_as_log = __esm({
31191
31312
  message: `println() as logger at line ${line}`,
31192
31313
  line,
31193
31314
  column: 1,
31194
- 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."
31315
+ 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)."
31195
31316
  });
31196
31317
  }
31197
31318
  return issues;
@@ -41036,14 +41157,14 @@ var init_implicitly_unwrapped_optional = __esm({
41036
41157
  });
41037
41158
 
41038
41159
  // src/rules/swift/print-debug.ts
41039
- var PRINT_REGEX, DEFAULT_THRESHOLD2, TEST_FILE_REGEX, swiftPrintDebugRule;
41160
+ var PRINT_REGEX, DEFAULT_THRESHOLD2, TEST_FILE_REGEX2, swiftPrintDebugRule;
41040
41161
  var init_print_debug = __esm({
41041
41162
  "src/rules/swift/print-debug.ts"() {
41042
41163
  "use strict";
41043
41164
  init_rule();
41044
41165
  PRINT_REGEX = /\bprint\s*\(/g;
41045
41166
  DEFAULT_THRESHOLD2 = 1;
41046
- TEST_FILE_REGEX = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
41167
+ TEST_FILE_REGEX2 = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
41047
41168
  swiftPrintDebugRule = createRule({
41048
41169
  id: "swift/print-debug",
41049
41170
  category: "typo",
@@ -41058,7 +41179,7 @@ var init_print_debug = __esm({
41058
41179
  const source = facts.v2?._source;
41059
41180
  if (!source) return issues;
41060
41181
  if (!/\.swift$/i.test(facts.filePath)) return issues;
41061
- if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
41182
+ if (TEST_FILE_REGEX2.test(facts.filePath)) return issues;
41062
41183
  const matches = [];
41063
41184
  let m;
41064
41185
  PRINT_REGEX.lastIndex = 0;
@@ -51530,7 +51651,7 @@ var init_signal_strength = __esm({
51530
51651
  precision: 0.1268,
51531
51652
  lastCalibratedAt: "2026-07-03T00:00:00Z",
51532
51653
  verdict: "OK",
51533
- _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).",
51654
+ _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).",
51534
51655
  aiSpecific: false,
51535
51656
  _v9Verdict: "OK",
51536
51657
  _v9Lift: 1.84,
@@ -51594,7 +51715,7 @@ var init_signal_strength = __esm({
51594
51715
  precision: 0.1091,
51595
51716
  lastCalibratedAt: "2026-07-03T00:00:00Z",
51596
51717
  verdict: "DORMANT",
51597
- _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.",
51718
+ _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.",
51598
51719
  aiSpecific: false,
51599
51720
  _v9Verdict: "DORMANT",
51600
51721
  _v9Lift: 0.97,
@@ -51803,7 +51924,7 @@ var init_signal_strength = __esm({
51803
51924
  precision: 0.2325,
51804
51925
  lastCalibratedAt: "2026-07-03T00:00:00Z",
51805
51926
  verdict: "DORMANT",
51806
- _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.",
51927
+ _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.",
51807
51928
  aiSpecific: true,
51808
51929
  _v7Verdict: "DORMANT",
51809
51930
  _v7Lift: 1,
@@ -51849,7 +51970,7 @@ var init_signal_strength = __esm({
51849
51970
  precision: 0.2187,
51850
51971
  lastCalibratedAt: "2026-07-03T00:00:00Z",
51851
51972
  verdict: "DORMANT",
51852
- _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.",
51973
+ _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.",
51853
51974
  aiSpecific: true,
51854
51975
  _v7Verdict: "DORMANT",
51855
51976
  _v7Lift: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopbrick",
3
- "version": "0.34.2",
3
+ "version": "0.34.6",
4
4
  "description": "Discovered, modeled, and governed repository structure. SlopBrick scans source code, classifies it against 95+ rules in 15 categories, computes 4 scores (aiSlopScore: lower=cleaner, engineeringHygiene, security, repositoryHealth composite), and persists the structure for AI agents and CI.",
5
5
  "type": "module",
6
6
  "bin": {