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/engine/worker.cjs +143 -22
- package/dist/engine/worker.js +143 -22
- package/dist/index.cjs +148 -27
- package/dist/index.js +148 -27
- package/package.json +1 -1
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.
|
|
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 -
|
|
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.
|
|
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(
|
|
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(
|
|
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.
|
|
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
|
|
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
|
-
|
|
30865
|
-
|
|
30866
|
-
|
|
30867
|
-
|
|
30868
|
-
|
|
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.
|
|
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 (
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
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": {
|