slopbrick 0.25.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37840,19 +37840,24 @@ var DEFAULT_CONFIG3 = {
37840
37840
  * - `tests/rules/**` — rule test files contain expected-issue
37841
37841
  * assertions, also meta-code.
37842
37842
  *
37843
- * Three patterns, ~70 issues removed per self-scan. Combined with
37844
- * the v0.25.0 graded security cap, this restores the v9 plan's
37845
- * "security 80" criterion (unachievable in v0.24.0 due to 90
37846
- * self-scan FPs collapsing the score to 0).
37843
+ * Three patterns (broadened in v0.25.1 from the v0.25.0 narrow
37844
+ * set, which missed `tests/engine/**`, `tests/cli/**`, and
37845
+ * `snippet/**`), ~80 issues removed per self-scan. Combined
37846
+ * with the v0.25.0 graded security cap, this restores the
37847
+ * v9 plan's "security ≥ 80" criterion (unachievable in
37848
+ * v0.24.0 due to 90 self-scan FPs collapsing the score to 0).
37847
37849
  *
37848
37850
  * Set `selfScan: { excludePaths: [] }` in `slopbrick.config.mjs`
37849
37851
  * to opt out and scan every file (legacy behavior).
37850
37852
  */
37851
37853
  selfScan: {
37852
37854
  excludePaths: [
37853
- "src/rules/**",
37854
- "tests/fixtures/**",
37855
- "tests/rules/**"
37855
+ "**/src/rules/**",
37856
+ // rule definitions are meta-code
37857
+ "**/snippet/**",
37858
+ // RULE_HINTS examples are intentional bad code
37859
+ "**/tests/**"
37860
+ // all test files (fixtures + unit + integration)
37856
37861
  ]
37857
37862
  }
37858
37863
  };
@@ -43856,13 +43861,13 @@ var signal_strength_default = {
43856
43861
  defaultOff: true
43857
43862
  },
43858
43863
  "java/arraylist-vs-linkedlist": {
43859
- recall: 0,
43860
- fpRate: 0,
43861
- ratio: 1,
43862
- precision: 0,
43863
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43864
+ recall: 35e-4,
43865
+ fpRate: 94e-4,
43866
+ ratio: 0.37,
43867
+ precision: 0.0446,
43868
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43864
43869
  verdict: "DORMANT",
43865
- _calibrationNote: "v0.20: new rule (new LinkedList<>() \u2014 use ArrayList (Effective Java Item 28)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43870
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files = 92196 total. v9 TP=36, FP=771, P=4.46%, FPR=0.94%, recall=0.35%, ratio=0.37, lift=P/FPR=4.74. Rule fires more on legacy enterprise Java (neg) than on AI/ML-integration Java (pos) because modern Java idiom defaults to ArrayList. v0.20: new rule (new LinkedList<>() \u2014 use ArrayList, Effective Java Item 28).",
43866
43871
  aiSpecific: true,
43867
43872
  _v7Verdict: "DORMANT",
43868
43873
  _v7Lift: 1,
@@ -43871,16 +43876,21 @@ var signal_strength_default = {
43871
43876
  _v7Precision: 0,
43872
43877
  _v8Verdict: "DORMANT",
43873
43878
  _v8Lift: 1,
43879
+ _v9Verdict: "DORMANT",
43880
+ _v9Lift: 4.74,
43881
+ _v9Recall: 35e-4,
43882
+ _v9FpRate: 94e-4,
43883
+ _v9Precision: 0.0446,
43874
43884
  defaultOff: true
43875
43885
  },
43876
43886
  "java/empty-catch-block": {
43877
- recall: 0,
43878
- fpRate: 0,
43879
- ratio: 1,
43880
- precision: 0,
43881
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43887
+ recall: 8e-3,
43888
+ fpRate: 0.1118,
43889
+ ratio: 0.07,
43890
+ precision: 89e-4,
43891
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43882
43892
  verdict: "DORMANT",
43883
- _calibrationNote: "v0.20: new rule (Empty catch block \u2014 silently swallows exceptions). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43893
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=82, FP=9159, P=0.89%, FPR=11.18%, recall=0.80%, ratio=0.07, lift=P/FPR=0.08. Strongly INVERTED: empty catch blocks are 14x more common in legacy enterprise Java than in AI/Java-modern pos repos. Heuristic likely needs to be tightened (e.g., require at least a comment) before it can serve as a meaningful discriminator. v0.20: new rule.",
43884
43894
  aiSpecific: true,
43885
43895
  _v7Verdict: "DORMANT",
43886
43896
  _v7Lift: 1,
@@ -43889,16 +43899,21 @@ var signal_strength_default = {
43889
43899
  _v7Precision: 0,
43890
43900
  _v8Verdict: "DORMANT",
43891
43901
  _v8Lift: 1,
43902
+ _v9Verdict: "DORMANT",
43903
+ _v9Lift: 0.08,
43904
+ _v9Recall: 8e-3,
43905
+ _v9FpRate: 0.1118,
43906
+ _v9Precision: 89e-4,
43892
43907
  defaultOff: true
43893
43908
  },
43894
43909
  "java/legacy-date-api": {
43895
- recall: 0,
43896
- fpRate: 0,
43897
- ratio: 1,
43898
- precision: 0,
43899
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43910
+ recall: 0.0285,
43911
+ fpRate: 0.0511,
43912
+ ratio: 0.56,
43913
+ precision: 0.0657,
43914
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43900
43915
  verdict: "DORMANT",
43901
- _calibrationNote: "v0.20: new rule (Legacy java.util.Date / Calendar \u2014 use java.time (JSR-310)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43916
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=294, FP=4181, P=6.57%, FPR=5.11%, recall=2.85%, ratio=0.56, lift=P/FPR=1.29. INVERTED-to-NOISY: legacy date API is more common in older enterprise Java (neg) than in modern AI/Java repos (pos). The heuristic correctly flags the legacy pattern, but the legacy pattern is over-represented in the neg baseline (older codebases). v0.20: new rule.",
43902
43917
  aiSpecific: true,
43903
43918
  _v7Verdict: "DORMANT",
43904
43919
  _v7Lift: 1,
@@ -43907,16 +43922,21 @@ var signal_strength_default = {
43907
43922
  _v7Precision: 0,
43908
43923
  _v8Verdict: "DORMANT",
43909
43924
  _v8Lift: 1,
43925
+ _v9Verdict: "DORMANT",
43926
+ _v9Lift: 1.29,
43927
+ _v9Recall: 0.0285,
43928
+ _v9FpRate: 0.0511,
43929
+ _v9Precision: 0.0657,
43910
43930
  defaultOff: true
43911
43931
  },
43912
43932
  "java/raw-type-overuse": {
43913
- recall: 0,
43914
- fpRate: 0,
43915
- ratio: 1,
43916
- precision: 0,
43917
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43933
+ recall: 0.1093,
43934
+ fpRate: 0.2041,
43935
+ ratio: 0.54,
43936
+ precision: 0.0632,
43937
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43918
43938
  verdict: "DORMANT",
43919
- _calibrationNote: "v0.20: new rule (Raw type usage \u2014 use generics). DORMANT until v9 Java corpus calibration.",
43939
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1126, FP=16712, P=6.32%, FPR=20.41%, recall=10.93%, ratio=0.54, lift=P/FPR=0.31. Strongly INVERTED: raw types (no generics) are 2x more common in legacy enterprise Java (neg) than in modern Java repos (pos). Heuristic is correctly identifying the legacy anti-pattern, but it's an age marker more than an AI-fingerprint. v0.20: new rule.",
43920
43940
  aiSpecific: true,
43921
43941
  _v7Verdict: "DORMANT",
43922
43942
  _v7Lift: 1,
@@ -43925,16 +43945,21 @@ var signal_strength_default = {
43925
43945
  _v7Precision: 0,
43926
43946
  _v8Verdict: "DORMANT",
43927
43947
  _v8Lift: 1,
43948
+ _v9Verdict: "DORMANT",
43949
+ _v9Lift: 0.31,
43950
+ _v9Recall: 0.1093,
43951
+ _v9FpRate: 0.2041,
43952
+ _v9Precision: 0.0632,
43928
43953
  defaultOff: true
43929
43954
  },
43930
43955
  "java/string-concat-loop": {
43931
- recall: 0,
43932
- fpRate: 0,
43933
- ratio: 1,
43934
- precision: 0,
43935
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43956
+ recall: 46e-4,
43957
+ fpRate: 0.0358,
43958
+ ratio: 0.13,
43959
+ precision: 0.0158,
43960
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43936
43961
  verdict: "DORMANT",
43937
- _calibrationNote: "v0.20: new rule (String concat in loop \u2014 use StringBuilder). DORMANT until v9 Java corpus calibration.",
43962
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=47, FP=2933, P=1.58%, FPR=3.58%, recall=0.46%, ratio=0.13, lift=P/FPR=0.44. Strongly INVERTED: string concatenation in a loop is 8x more common in legacy enterprise Java (neg) than in AI/ML/Java-modern pos repos. Modern Java training data and modern IDE warnings discourage the pattern. v0.20: new rule.",
43938
43963
  aiSpecific: true,
43939
43964
  _v7Verdict: "DORMANT",
43940
43965
  _v7Lift: 1,
@@ -43943,16 +43968,21 @@ var signal_strength_default = {
43943
43968
  _v7Precision: 0,
43944
43969
  _v8Verdict: "DORMANT",
43945
43970
  _v8Lift: 1,
43971
+ _v9Verdict: "DORMANT",
43972
+ _v9Lift: 0.44,
43973
+ _v9Recall: 46e-4,
43974
+ _v9FpRate: 0.0358,
43975
+ _v9Precision: 0.0158,
43946
43976
  defaultOff: true
43947
43977
  },
43948
43978
  "java/system-out-println": {
43949
- recall: 0,
43950
- fpRate: 0,
43951
- ratio: 1,
43952
- precision: 0,
43953
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43979
+ recall: 0.1503,
43980
+ fpRate: 0.2533,
43981
+ ratio: 0.59,
43982
+ precision: 0.0695,
43983
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43954
43984
  verdict: "DORMANT",
43955
- _calibrationNote: "v0.20: new rule (System.out.println in production code \u2014 use a logger). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43985
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1549, FP=20737, P=6.95%, FPR=25.32%, recall=15.03%, ratio=0.59, lift=P/FPR=0.27. INVERTED: System.out.println is more common in legacy enterprise Java (neg) than in modern AI/Java repos (pos). Modern Java defaults to SLF4J/Log4j, AI-generated Java starts with a logger import. The pattern is the opposite of the v0.20 design intent (which assumed println was the AI-fingerprint signal). v0.25.x: consider gating this rule by Java era (recent file mtime) or making it an age-fingerprint rather than AI-fingerprint. v0.20: new rule.",
43956
43986
  aiSpecific: true,
43957
43987
  _v7Verdict: "DORMANT",
43958
43988
  _v7Lift: 1,
@@ -43961,6 +43991,11 @@ var signal_strength_default = {
43961
43991
  _v7Precision: 0,
43962
43992
  _v8Verdict: "DORMANT",
43963
43993
  _v8Lift: 1,
43994
+ _v9Verdict: "DORMANT",
43995
+ _v9Lift: 0.27,
43996
+ _v9Recall: 0.1503,
43997
+ _v9FpRate: 0.2533,
43998
+ _v9Precision: 0.0695,
43964
43999
  defaultOff: true
43965
44000
  },
43966
44001
  "layout/forced-layout": {
@@ -37811,19 +37811,24 @@ var DEFAULT_CONFIG3 = {
37811
37811
  * - `tests/rules/**` — rule test files contain expected-issue
37812
37812
  * assertions, also meta-code.
37813
37813
  *
37814
- * Three patterns, ~70 issues removed per self-scan. Combined with
37815
- * the v0.25.0 graded security cap, this restores the v9 plan's
37816
- * "security 80" criterion (unachievable in v0.24.0 due to 90
37817
- * self-scan FPs collapsing the score to 0).
37814
+ * Three patterns (broadened in v0.25.1 from the v0.25.0 narrow
37815
+ * set, which missed `tests/engine/**`, `tests/cli/**`, and
37816
+ * `snippet/**`), ~80 issues removed per self-scan. Combined
37817
+ * with the v0.25.0 graded security cap, this restores the
37818
+ * v9 plan's "security ≥ 80" criterion (unachievable in
37819
+ * v0.24.0 due to 90 self-scan FPs collapsing the score to 0).
37818
37820
  *
37819
37821
  * Set `selfScan: { excludePaths: [] }` in `slopbrick.config.mjs`
37820
37822
  * to opt out and scan every file (legacy behavior).
37821
37823
  */
37822
37824
  selfScan: {
37823
37825
  excludePaths: [
37824
- "src/rules/**",
37825
- "tests/fixtures/**",
37826
- "tests/rules/**"
37826
+ "**/src/rules/**",
37827
+ // rule definitions are meta-code
37828
+ "**/snippet/**",
37829
+ // RULE_HINTS examples are intentional bad code
37830
+ "**/tests/**"
37831
+ // all test files (fixtures + unit + integration)
37827
37832
  ]
37828
37833
  }
37829
37834
  };
@@ -43827,13 +43832,13 @@ var signal_strength_default = {
43827
43832
  defaultOff: true
43828
43833
  },
43829
43834
  "java/arraylist-vs-linkedlist": {
43830
- recall: 0,
43831
- fpRate: 0,
43832
- ratio: 1,
43833
- precision: 0,
43834
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43835
+ recall: 35e-4,
43836
+ fpRate: 94e-4,
43837
+ ratio: 0.37,
43838
+ precision: 0.0446,
43839
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43835
43840
  verdict: "DORMANT",
43836
- _calibrationNote: "v0.20: new rule (new LinkedList<>() \u2014 use ArrayList (Effective Java Item 28)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43841
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files = 92196 total. v9 TP=36, FP=771, P=4.46%, FPR=0.94%, recall=0.35%, ratio=0.37, lift=P/FPR=4.74. Rule fires more on legacy enterprise Java (neg) than on AI/ML-integration Java (pos) because modern Java idiom defaults to ArrayList. v0.20: new rule (new LinkedList<>() \u2014 use ArrayList, Effective Java Item 28).",
43837
43842
  aiSpecific: true,
43838
43843
  _v7Verdict: "DORMANT",
43839
43844
  _v7Lift: 1,
@@ -43842,16 +43847,21 @@ var signal_strength_default = {
43842
43847
  _v7Precision: 0,
43843
43848
  _v8Verdict: "DORMANT",
43844
43849
  _v8Lift: 1,
43850
+ _v9Verdict: "DORMANT",
43851
+ _v9Lift: 4.74,
43852
+ _v9Recall: 35e-4,
43853
+ _v9FpRate: 94e-4,
43854
+ _v9Precision: 0.0446,
43845
43855
  defaultOff: true
43846
43856
  },
43847
43857
  "java/empty-catch-block": {
43848
- recall: 0,
43849
- fpRate: 0,
43850
- ratio: 1,
43851
- precision: 0,
43852
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43858
+ recall: 8e-3,
43859
+ fpRate: 0.1118,
43860
+ ratio: 0.07,
43861
+ precision: 89e-4,
43862
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43853
43863
  verdict: "DORMANT",
43854
- _calibrationNote: "v0.20: new rule (Empty catch block \u2014 silently swallows exceptions). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43864
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=82, FP=9159, P=0.89%, FPR=11.18%, recall=0.80%, ratio=0.07, lift=P/FPR=0.08. Strongly INVERTED: empty catch blocks are 14x more common in legacy enterprise Java than in AI/Java-modern pos repos. Heuristic likely needs to be tightened (e.g., require at least a comment) before it can serve as a meaningful discriminator. v0.20: new rule.",
43855
43865
  aiSpecific: true,
43856
43866
  _v7Verdict: "DORMANT",
43857
43867
  _v7Lift: 1,
@@ -43860,16 +43870,21 @@ var signal_strength_default = {
43860
43870
  _v7Precision: 0,
43861
43871
  _v8Verdict: "DORMANT",
43862
43872
  _v8Lift: 1,
43873
+ _v9Verdict: "DORMANT",
43874
+ _v9Lift: 0.08,
43875
+ _v9Recall: 8e-3,
43876
+ _v9FpRate: 0.1118,
43877
+ _v9Precision: 89e-4,
43863
43878
  defaultOff: true
43864
43879
  },
43865
43880
  "java/legacy-date-api": {
43866
- recall: 0,
43867
- fpRate: 0,
43868
- ratio: 1,
43869
- precision: 0,
43870
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43881
+ recall: 0.0285,
43882
+ fpRate: 0.0511,
43883
+ ratio: 0.56,
43884
+ precision: 0.0657,
43885
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43871
43886
  verdict: "DORMANT",
43872
- _calibrationNote: "v0.20: new rule (Legacy java.util.Date / Calendar \u2014 use java.time (JSR-310)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43887
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=294, FP=4181, P=6.57%, FPR=5.11%, recall=2.85%, ratio=0.56, lift=P/FPR=1.29. INVERTED-to-NOISY: legacy date API is more common in older enterprise Java (neg) than in modern AI/Java repos (pos). The heuristic correctly flags the legacy pattern, but the legacy pattern is over-represented in the neg baseline (older codebases). v0.20: new rule.",
43873
43888
  aiSpecific: true,
43874
43889
  _v7Verdict: "DORMANT",
43875
43890
  _v7Lift: 1,
@@ -43878,16 +43893,21 @@ var signal_strength_default = {
43878
43893
  _v7Precision: 0,
43879
43894
  _v8Verdict: "DORMANT",
43880
43895
  _v8Lift: 1,
43896
+ _v9Verdict: "DORMANT",
43897
+ _v9Lift: 1.29,
43898
+ _v9Recall: 0.0285,
43899
+ _v9FpRate: 0.0511,
43900
+ _v9Precision: 0.0657,
43881
43901
  defaultOff: true
43882
43902
  },
43883
43903
  "java/raw-type-overuse": {
43884
- recall: 0,
43885
- fpRate: 0,
43886
- ratio: 1,
43887
- precision: 0,
43888
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43904
+ recall: 0.1093,
43905
+ fpRate: 0.2041,
43906
+ ratio: 0.54,
43907
+ precision: 0.0632,
43908
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43889
43909
  verdict: "DORMANT",
43890
- _calibrationNote: "v0.20: new rule (Raw type usage \u2014 use generics). DORMANT until v9 Java corpus calibration.",
43910
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1126, FP=16712, P=6.32%, FPR=20.41%, recall=10.93%, ratio=0.54, lift=P/FPR=0.31. Strongly INVERTED: raw types (no generics) are 2x more common in legacy enterprise Java (neg) than in modern Java repos (pos). Heuristic is correctly identifying the legacy anti-pattern, but it's an age marker more than an AI-fingerprint. v0.20: new rule.",
43891
43911
  aiSpecific: true,
43892
43912
  _v7Verdict: "DORMANT",
43893
43913
  _v7Lift: 1,
@@ -43896,16 +43916,21 @@ var signal_strength_default = {
43896
43916
  _v7Precision: 0,
43897
43917
  _v8Verdict: "DORMANT",
43898
43918
  _v8Lift: 1,
43919
+ _v9Verdict: "DORMANT",
43920
+ _v9Lift: 0.31,
43921
+ _v9Recall: 0.1093,
43922
+ _v9FpRate: 0.2041,
43923
+ _v9Precision: 0.0632,
43899
43924
  defaultOff: true
43900
43925
  },
43901
43926
  "java/string-concat-loop": {
43902
- recall: 0,
43903
- fpRate: 0,
43904
- ratio: 1,
43905
- precision: 0,
43906
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43927
+ recall: 46e-4,
43928
+ fpRate: 0.0358,
43929
+ ratio: 0.13,
43930
+ precision: 0.0158,
43931
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43907
43932
  verdict: "DORMANT",
43908
- _calibrationNote: "v0.20: new rule (String concat in loop \u2014 use StringBuilder). DORMANT until v9 Java corpus calibration.",
43933
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=47, FP=2933, P=1.58%, FPR=3.58%, recall=0.46%, ratio=0.13, lift=P/FPR=0.44. Strongly INVERTED: string concatenation in a loop is 8x more common in legacy enterprise Java (neg) than in AI/ML/Java-modern pos repos. Modern Java training data and modern IDE warnings discourage the pattern. v0.20: new rule.",
43909
43934
  aiSpecific: true,
43910
43935
  _v7Verdict: "DORMANT",
43911
43936
  _v7Lift: 1,
@@ -43914,16 +43939,21 @@ var signal_strength_default = {
43914
43939
  _v7Precision: 0,
43915
43940
  _v8Verdict: "DORMANT",
43916
43941
  _v8Lift: 1,
43942
+ _v9Verdict: "DORMANT",
43943
+ _v9Lift: 0.44,
43944
+ _v9Recall: 46e-4,
43945
+ _v9FpRate: 0.0358,
43946
+ _v9Precision: 0.0158,
43917
43947
  defaultOff: true
43918
43948
  },
43919
43949
  "java/system-out-println": {
43920
- recall: 0,
43921
- fpRate: 0,
43922
- ratio: 1,
43923
- precision: 0,
43924
- lastCalibratedAt: "2026-07-01T00:00:00Z",
43950
+ recall: 0.1503,
43951
+ fpRate: 0.2533,
43952
+ ratio: 0.59,
43953
+ precision: 0.0695,
43954
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
43925
43955
  verdict: "DORMANT",
43926
- _calibrationNote: "v0.20: new rule (System.out.println in production code \u2014 use a logger). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
43956
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1549, FP=20737, P=6.95%, FPR=25.32%, recall=15.03%, ratio=0.59, lift=P/FPR=0.27. INVERTED: System.out.println is more common in legacy enterprise Java (neg) than in modern AI/Java repos (pos). Modern Java defaults to SLF4J/Log4j, AI-generated Java starts with a logger import. The pattern is the opposite of the v0.20 design intent (which assumed println was the AI-fingerprint signal). v0.25.x: consider gating this rule by Java era (recent file mtime) or making it an age-fingerprint rather than AI-fingerprint. v0.20: new rule.",
43927
43957
  aiSpecific: true,
43928
43958
  _v7Verdict: "DORMANT",
43929
43959
  _v7Lift: 1,
@@ -43932,6 +43962,11 @@ var signal_strength_default = {
43932
43962
  _v7Precision: 0,
43933
43963
  _v8Verdict: "DORMANT",
43934
43964
  _v8Lift: 1,
43965
+ _v9Verdict: "DORMANT",
43966
+ _v9Lift: 0.27,
43967
+ _v9Recall: 0.1503,
43968
+ _v9FpRate: 0.2533,
43969
+ _v9Precision: 0.0695,
43935
43970
  defaultOff: true
43936
43971
  },
43937
43972
  "layout/forced-layout": {
package/dist/index.cjs CHANGED
@@ -36,7 +36,7 @@ var VERSION;
36
36
  var init_header = __esm({
37
37
  "src/types/_header.ts"() {
38
38
  "use strict";
39
- VERSION = "0.25.0";
39
+ VERSION = "0.25.1";
40
40
  }
41
41
  });
42
42
 
@@ -344,19 +344,24 @@ var init_defaults = __esm({
344
344
  * - `tests/rules/**` — rule test files contain expected-issue
345
345
  * assertions, also meta-code.
346
346
  *
347
- * Three patterns, ~70 issues removed per self-scan. Combined with
348
- * the v0.25.0 graded security cap, this restores the v9 plan's
349
- * "security 80" criterion (unachievable in v0.24.0 due to 90
350
- * self-scan FPs collapsing the score to 0).
347
+ * Three patterns (broadened in v0.25.1 from the v0.25.0 narrow
348
+ * set, which missed `tests/engine/**`, `tests/cli/**`, and
349
+ * `snippet/**`), ~80 issues removed per self-scan. Combined
350
+ * with the v0.25.0 graded security cap, this restores the
351
+ * v9 plan's "security ≥ 80" criterion (unachievable in
352
+ * v0.24.0 due to 90 self-scan FPs collapsing the score to 0).
351
353
  *
352
354
  * Set `selfScan: { excludePaths: [] }` in `slopbrick.config.mjs`
353
355
  * to opt out and scan every file (legacy behavior).
354
356
  */
355
357
  selfScan: {
356
358
  excludePaths: [
357
- "src/rules/**",
358
- "tests/fixtures/**",
359
- "tests/rules/**"
359
+ "**/src/rules/**",
360
+ // rule definitions are meta-code
361
+ "**/snippet/**",
362
+ // RULE_HINTS examples are intentional bad code
363
+ "**/tests/**"
364
+ // all test files (fixtures + unit + integration)
360
365
  ]
361
366
  }
362
367
  };
@@ -49911,13 +49916,13 @@ var init_signal_strength = __esm({
49911
49916
  defaultOff: true
49912
49917
  },
49913
49918
  "java/arraylist-vs-linkedlist": {
49914
- recall: 0,
49915
- fpRate: 0,
49916
- ratio: 1,
49917
- precision: 0,
49918
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49919
+ recall: 35e-4,
49920
+ fpRate: 94e-4,
49921
+ ratio: 0.37,
49922
+ precision: 0.0446,
49923
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49919
49924
  verdict: "DORMANT",
49920
- _calibrationNote: "v0.20: new rule (new LinkedList<>() \u2014 use ArrayList (Effective Java Item 28)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
49925
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files = 92196 total. v9 TP=36, FP=771, P=4.46%, FPR=0.94%, recall=0.35%, ratio=0.37, lift=P/FPR=4.74. Rule fires more on legacy enterprise Java (neg) than on AI/ML-integration Java (pos) because modern Java idiom defaults to ArrayList. v0.20: new rule (new LinkedList<>() \u2014 use ArrayList, Effective Java Item 28).",
49921
49926
  aiSpecific: true,
49922
49927
  _v7Verdict: "DORMANT",
49923
49928
  _v7Lift: 1,
@@ -49926,16 +49931,21 @@ var init_signal_strength = __esm({
49926
49931
  _v7Precision: 0,
49927
49932
  _v8Verdict: "DORMANT",
49928
49933
  _v8Lift: 1,
49934
+ _v9Verdict: "DORMANT",
49935
+ _v9Lift: 4.74,
49936
+ _v9Recall: 35e-4,
49937
+ _v9FpRate: 94e-4,
49938
+ _v9Precision: 0.0446,
49929
49939
  defaultOff: true
49930
49940
  },
49931
49941
  "java/empty-catch-block": {
49932
- recall: 0,
49933
- fpRate: 0,
49934
- ratio: 1,
49935
- precision: 0,
49936
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49942
+ recall: 8e-3,
49943
+ fpRate: 0.1118,
49944
+ ratio: 0.07,
49945
+ precision: 89e-4,
49946
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49937
49947
  verdict: "DORMANT",
49938
- _calibrationNote: "v0.20: new rule (Empty catch block \u2014 silently swallows exceptions). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
49948
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=82, FP=9159, P=0.89%, FPR=11.18%, recall=0.80%, ratio=0.07, lift=P/FPR=0.08. Strongly INVERTED: empty catch blocks are 14x more common in legacy enterprise Java than in AI/Java-modern pos repos. Heuristic likely needs to be tightened (e.g., require at least a comment) before it can serve as a meaningful discriminator. v0.20: new rule.",
49939
49949
  aiSpecific: true,
49940
49950
  _v7Verdict: "DORMANT",
49941
49951
  _v7Lift: 1,
@@ -49944,16 +49954,21 @@ var init_signal_strength = __esm({
49944
49954
  _v7Precision: 0,
49945
49955
  _v8Verdict: "DORMANT",
49946
49956
  _v8Lift: 1,
49957
+ _v9Verdict: "DORMANT",
49958
+ _v9Lift: 0.08,
49959
+ _v9Recall: 8e-3,
49960
+ _v9FpRate: 0.1118,
49961
+ _v9Precision: 89e-4,
49947
49962
  defaultOff: true
49948
49963
  },
49949
49964
  "java/legacy-date-api": {
49950
- recall: 0,
49951
- fpRate: 0,
49952
- ratio: 1,
49953
- precision: 0,
49954
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49965
+ recall: 0.0285,
49966
+ fpRate: 0.0511,
49967
+ ratio: 0.56,
49968
+ precision: 0.0657,
49969
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49955
49970
  verdict: "DORMANT",
49956
- _calibrationNote: "v0.20: new rule (Legacy java.util.Date / Calendar \u2014 use java.time (JSR-310)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
49971
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=294, FP=4181, P=6.57%, FPR=5.11%, recall=2.85%, ratio=0.56, lift=P/FPR=1.29. INVERTED-to-NOISY: legacy date API is more common in older enterprise Java (neg) than in modern AI/Java repos (pos). The heuristic correctly flags the legacy pattern, but the legacy pattern is over-represented in the neg baseline (older codebases). v0.20: new rule.",
49957
49972
  aiSpecific: true,
49958
49973
  _v7Verdict: "DORMANT",
49959
49974
  _v7Lift: 1,
@@ -49962,16 +49977,21 @@ var init_signal_strength = __esm({
49962
49977
  _v7Precision: 0,
49963
49978
  _v8Verdict: "DORMANT",
49964
49979
  _v8Lift: 1,
49980
+ _v9Verdict: "DORMANT",
49981
+ _v9Lift: 1.29,
49982
+ _v9Recall: 0.0285,
49983
+ _v9FpRate: 0.0511,
49984
+ _v9Precision: 0.0657,
49965
49985
  defaultOff: true
49966
49986
  },
49967
49987
  "java/raw-type-overuse": {
49968
- recall: 0,
49969
- fpRate: 0,
49970
- ratio: 1,
49971
- precision: 0,
49972
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49988
+ recall: 0.1093,
49989
+ fpRate: 0.2041,
49990
+ ratio: 0.54,
49991
+ precision: 0.0632,
49992
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49973
49993
  verdict: "DORMANT",
49974
- _calibrationNote: "v0.20: new rule (Raw type usage \u2014 use generics). DORMANT until v9 Java corpus calibration.",
49994
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1126, FP=16712, P=6.32%, FPR=20.41%, recall=10.93%, ratio=0.54, lift=P/FPR=0.31. Strongly INVERTED: raw types (no generics) are 2x more common in legacy enterprise Java (neg) than in modern Java repos (pos). Heuristic is correctly identifying the legacy anti-pattern, but it's an age marker more than an AI-fingerprint. v0.20: new rule.",
49975
49995
  aiSpecific: true,
49976
49996
  _v7Verdict: "DORMANT",
49977
49997
  _v7Lift: 1,
@@ -49980,16 +50000,21 @@ var init_signal_strength = __esm({
49980
50000
  _v7Precision: 0,
49981
50001
  _v8Verdict: "DORMANT",
49982
50002
  _v8Lift: 1,
50003
+ _v9Verdict: "DORMANT",
50004
+ _v9Lift: 0.31,
50005
+ _v9Recall: 0.1093,
50006
+ _v9FpRate: 0.2041,
50007
+ _v9Precision: 0.0632,
49983
50008
  defaultOff: true
49984
50009
  },
49985
50010
  "java/string-concat-loop": {
49986
- recall: 0,
49987
- fpRate: 0,
49988
- ratio: 1,
49989
- precision: 0,
49990
- lastCalibratedAt: "2026-07-01T00:00:00Z",
50011
+ recall: 46e-4,
50012
+ fpRate: 0.0358,
50013
+ ratio: 0.13,
50014
+ precision: 0.0158,
50015
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49991
50016
  verdict: "DORMANT",
49992
- _calibrationNote: "v0.20: new rule (String concat in loop \u2014 use StringBuilder). DORMANT until v9 Java corpus calibration.",
50017
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=47, FP=2933, P=1.58%, FPR=3.58%, recall=0.46%, ratio=0.13, lift=P/FPR=0.44. Strongly INVERTED: string concatenation in a loop is 8x more common in legacy enterprise Java (neg) than in AI/ML/Java-modern pos repos. Modern Java training data and modern IDE warnings discourage the pattern. v0.20: new rule.",
49993
50018
  aiSpecific: true,
49994
50019
  _v7Verdict: "DORMANT",
49995
50020
  _v7Lift: 1,
@@ -49998,16 +50023,21 @@ var init_signal_strength = __esm({
49998
50023
  _v7Precision: 0,
49999
50024
  _v8Verdict: "DORMANT",
50000
50025
  _v8Lift: 1,
50026
+ _v9Verdict: "DORMANT",
50027
+ _v9Lift: 0.44,
50028
+ _v9Recall: 46e-4,
50029
+ _v9FpRate: 0.0358,
50030
+ _v9Precision: 0.0158,
50001
50031
  defaultOff: true
50002
50032
  },
50003
50033
  "java/system-out-println": {
50004
- recall: 0,
50005
- fpRate: 0,
50006
- ratio: 1,
50007
- precision: 0,
50008
- lastCalibratedAt: "2026-07-01T00:00:00Z",
50034
+ recall: 0.1503,
50035
+ fpRate: 0.2533,
50036
+ ratio: 0.59,
50037
+ precision: 0.0695,
50038
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
50009
50039
  verdict: "DORMANT",
50010
- _calibrationNote: "v0.20: new rule (System.out.println in production code \u2014 use a logger). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
50040
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1549, FP=20737, P=6.95%, FPR=25.32%, recall=15.03%, ratio=0.59, lift=P/FPR=0.27. INVERTED: System.out.println is more common in legacy enterprise Java (neg) than in modern AI/Java repos (pos). Modern Java defaults to SLF4J/Log4j, AI-generated Java starts with a logger import. The pattern is the opposite of the v0.20 design intent (which assumed println was the AI-fingerprint signal). v0.25.x: consider gating this rule by Java era (recent file mtime) or making it an age-fingerprint rather than AI-fingerprint. v0.20: new rule.",
50011
50041
  aiSpecific: true,
50012
50042
  _v7Verdict: "DORMANT",
50013
50043
  _v7Lift: 1,
@@ -50016,6 +50046,11 @@ var init_signal_strength = __esm({
50016
50046
  _v7Precision: 0,
50017
50047
  _v8Verdict: "DORMANT",
50018
50048
  _v8Lift: 1,
50049
+ _v9Verdict: "DORMANT",
50050
+ _v9Lift: 0.27,
50051
+ _v9Recall: 0.1503,
50052
+ _v9FpRate: 0.2533,
50053
+ _v9Precision: 0.0695,
50019
50054
  defaultOff: true
50020
50055
  },
50021
50056
  "layout/forced-layout": {
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.25.0";
22
+ VERSION = "0.25.1";
23
23
  }
24
24
  });
25
25
 
@@ -327,19 +327,24 @@ var init_defaults = __esm({
327
327
  * - `tests/rules/**` — rule test files contain expected-issue
328
328
  * assertions, also meta-code.
329
329
  *
330
- * Three patterns, ~70 issues removed per self-scan. Combined with
331
- * the v0.25.0 graded security cap, this restores the v9 plan's
332
- * "security 80" criterion (unachievable in v0.24.0 due to 90
333
- * self-scan FPs collapsing the score to 0).
330
+ * Three patterns (broadened in v0.25.1 from the v0.25.0 narrow
331
+ * set, which missed `tests/engine/**`, `tests/cli/**`, and
332
+ * `snippet/**`), ~80 issues removed per self-scan. Combined
333
+ * with the v0.25.0 graded security cap, this restores the
334
+ * v9 plan's "security ≥ 80" criterion (unachievable in
335
+ * v0.24.0 due to 90 self-scan FPs collapsing the score to 0).
334
336
  *
335
337
  * Set `selfScan: { excludePaths: [] }` in `slopbrick.config.mjs`
336
338
  * to opt out and scan every file (legacy behavior).
337
339
  */
338
340
  selfScan: {
339
341
  excludePaths: [
340
- "src/rules/**",
341
- "tests/fixtures/**",
342
- "tests/rules/**"
342
+ "**/src/rules/**",
343
+ // rule definitions are meta-code
344
+ "**/snippet/**",
345
+ // RULE_HINTS examples are intentional bad code
346
+ "**/tests/**"
347
+ // all test files (fixtures + unit + integration)
343
348
  ]
344
349
  }
345
350
  };
@@ -49889,13 +49894,13 @@ var init_signal_strength = __esm({
49889
49894
  defaultOff: true
49890
49895
  },
49891
49896
  "java/arraylist-vs-linkedlist": {
49892
- recall: 0,
49893
- fpRate: 0,
49894
- ratio: 1,
49895
- precision: 0,
49896
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49897
+ recall: 35e-4,
49898
+ fpRate: 94e-4,
49899
+ ratio: 0.37,
49900
+ precision: 0.0446,
49901
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49897
49902
  verdict: "DORMANT",
49898
- _calibrationNote: "v0.20: new rule (new LinkedList<>() \u2014 use ArrayList (Effective Java Item 28)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
49903
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files = 92196 total. v9 TP=36, FP=771, P=4.46%, FPR=0.94%, recall=0.35%, ratio=0.37, lift=P/FPR=4.74. Rule fires more on legacy enterprise Java (neg) than on AI/ML-integration Java (pos) because modern Java idiom defaults to ArrayList. v0.20: new rule (new LinkedList<>() \u2014 use ArrayList, Effective Java Item 28).",
49899
49904
  aiSpecific: true,
49900
49905
  _v7Verdict: "DORMANT",
49901
49906
  _v7Lift: 1,
@@ -49904,16 +49909,21 @@ var init_signal_strength = __esm({
49904
49909
  _v7Precision: 0,
49905
49910
  _v8Verdict: "DORMANT",
49906
49911
  _v8Lift: 1,
49912
+ _v9Verdict: "DORMANT",
49913
+ _v9Lift: 4.74,
49914
+ _v9Recall: 35e-4,
49915
+ _v9FpRate: 94e-4,
49916
+ _v9Precision: 0.0446,
49907
49917
  defaultOff: true
49908
49918
  },
49909
49919
  "java/empty-catch-block": {
49910
- recall: 0,
49911
- fpRate: 0,
49912
- ratio: 1,
49913
- precision: 0,
49914
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49920
+ recall: 8e-3,
49921
+ fpRate: 0.1118,
49922
+ ratio: 0.07,
49923
+ precision: 89e-4,
49924
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49915
49925
  verdict: "DORMANT",
49916
- _calibrationNote: "v0.20: new rule (Empty catch block \u2014 silently swallows exceptions). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
49926
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=82, FP=9159, P=0.89%, FPR=11.18%, recall=0.80%, ratio=0.07, lift=P/FPR=0.08. Strongly INVERTED: empty catch blocks are 14x more common in legacy enterprise Java than in AI/Java-modern pos repos. Heuristic likely needs to be tightened (e.g., require at least a comment) before it can serve as a meaningful discriminator. v0.20: new rule.",
49917
49927
  aiSpecific: true,
49918
49928
  _v7Verdict: "DORMANT",
49919
49929
  _v7Lift: 1,
@@ -49922,16 +49932,21 @@ var init_signal_strength = __esm({
49922
49932
  _v7Precision: 0,
49923
49933
  _v8Verdict: "DORMANT",
49924
49934
  _v8Lift: 1,
49935
+ _v9Verdict: "DORMANT",
49936
+ _v9Lift: 0.08,
49937
+ _v9Recall: 8e-3,
49938
+ _v9FpRate: 0.1118,
49939
+ _v9Precision: 89e-4,
49925
49940
  defaultOff: true
49926
49941
  },
49927
49942
  "java/legacy-date-api": {
49928
- recall: 0,
49929
- fpRate: 0,
49930
- ratio: 1,
49931
- precision: 0,
49932
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49943
+ recall: 0.0285,
49944
+ fpRate: 0.0511,
49945
+ ratio: 0.56,
49946
+ precision: 0.0657,
49947
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49933
49948
  verdict: "DORMANT",
49934
- _calibrationNote: "v0.20: new rule (Legacy java.util.Date / Calendar \u2014 use java.time (JSR-310)). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
49949
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=294, FP=4181, P=6.57%, FPR=5.11%, recall=2.85%, ratio=0.56, lift=P/FPR=1.29. INVERTED-to-NOISY: legacy date API is more common in older enterprise Java (neg) than in modern AI/Java repos (pos). The heuristic correctly flags the legacy pattern, but the legacy pattern is over-represented in the neg baseline (older codebases). v0.20: new rule.",
49935
49950
  aiSpecific: true,
49936
49951
  _v7Verdict: "DORMANT",
49937
49952
  _v7Lift: 1,
@@ -49940,16 +49955,21 @@ var init_signal_strength = __esm({
49940
49955
  _v7Precision: 0,
49941
49956
  _v8Verdict: "DORMANT",
49942
49957
  _v8Lift: 1,
49958
+ _v9Verdict: "DORMANT",
49959
+ _v9Lift: 1.29,
49960
+ _v9Recall: 0.0285,
49961
+ _v9FpRate: 0.0511,
49962
+ _v9Precision: 0.0657,
49943
49963
  defaultOff: true
49944
49964
  },
49945
49965
  "java/raw-type-overuse": {
49946
- recall: 0,
49947
- fpRate: 0,
49948
- ratio: 1,
49949
- precision: 0,
49950
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49966
+ recall: 0.1093,
49967
+ fpRate: 0.2041,
49968
+ ratio: 0.54,
49969
+ precision: 0.0632,
49970
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49951
49971
  verdict: "DORMANT",
49952
- _calibrationNote: "v0.20: new rule (Raw type usage \u2014 use generics). DORMANT until v9 Java corpus calibration.",
49972
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1126, FP=16712, P=6.32%, FPR=20.41%, recall=10.93%, ratio=0.54, lift=P/FPR=0.31. Strongly INVERTED: raw types (no generics) are 2x more common in legacy enterprise Java (neg) than in modern Java repos (pos). Heuristic is correctly identifying the legacy anti-pattern, but it's an age marker more than an AI-fingerprint. v0.20: new rule.",
49953
49973
  aiSpecific: true,
49954
49974
  _v7Verdict: "DORMANT",
49955
49975
  _v7Lift: 1,
@@ -49958,16 +49978,21 @@ var init_signal_strength = __esm({
49958
49978
  _v7Precision: 0,
49959
49979
  _v8Verdict: "DORMANT",
49960
49980
  _v8Lift: 1,
49981
+ _v9Verdict: "DORMANT",
49982
+ _v9Lift: 0.31,
49983
+ _v9Recall: 0.1093,
49984
+ _v9FpRate: 0.2041,
49985
+ _v9Precision: 0.0632,
49961
49986
  defaultOff: true
49962
49987
  },
49963
49988
  "java/string-concat-loop": {
49964
- recall: 0,
49965
- fpRate: 0,
49966
- ratio: 1,
49967
- precision: 0,
49968
- lastCalibratedAt: "2026-07-01T00:00:00Z",
49989
+ recall: 46e-4,
49990
+ fpRate: 0.0358,
49991
+ ratio: 0.13,
49992
+ precision: 0.0158,
49993
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49969
49994
  verdict: "DORMANT",
49970
- _calibrationNote: "v0.20: new rule (String concat in loop \u2014 use StringBuilder). DORMANT until v9 Java corpus calibration.",
49995
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=47, FP=2933, P=1.58%, FPR=3.58%, recall=0.46%, ratio=0.13, lift=P/FPR=0.44. Strongly INVERTED: string concatenation in a loop is 8x more common in legacy enterprise Java (neg) than in AI/ML/Java-modern pos repos. Modern Java training data and modern IDE warnings discourage the pattern. v0.20: new rule.",
49971
49996
  aiSpecific: true,
49972
49997
  _v7Verdict: "DORMANT",
49973
49998
  _v7Lift: 1,
@@ -49976,16 +50001,21 @@ var init_signal_strength = __esm({
49976
50001
  _v7Precision: 0,
49977
50002
  _v8Verdict: "DORMANT",
49978
50003
  _v8Lift: 1,
50004
+ _v9Verdict: "DORMANT",
50005
+ _v9Lift: 0.44,
50006
+ _v9Recall: 46e-4,
50007
+ _v9FpRate: 0.0358,
50008
+ _v9Precision: 0.0158,
49979
50009
  defaultOff: true
49980
50010
  },
49981
50011
  "java/system-out-println": {
49982
- recall: 0,
49983
- fpRate: 0,
49984
- ratio: 1,
49985
- precision: 0,
49986
- lastCalibratedAt: "2026-07-01T00:00:00Z",
50012
+ recall: 0.1503,
50013
+ fpRate: 0.2533,
50014
+ ratio: 0.59,
50015
+ precision: 0.0695,
50016
+ lastCalibratedAt: "2026-07-02T19:34:00Z",
49987
50017
  verdict: "DORMANT",
49988
- _calibrationNote: "v0.20: new rule (System.out.println in production code \u2014 use a logger). Not yet calibrated. Scheduled for v9 Java corpus calibration.",
50018
+ _calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1549, FP=20737, P=6.95%, FPR=25.32%, recall=15.03%, ratio=0.59, lift=P/FPR=0.27. INVERTED: System.out.println is more common in legacy enterprise Java (neg) than in modern AI/Java repos (pos). Modern Java defaults to SLF4J/Log4j, AI-generated Java starts with a logger import. The pattern is the opposite of the v0.20 design intent (which assumed println was the AI-fingerprint signal). v0.25.x: consider gating this rule by Java era (recent file mtime) or making it an age-fingerprint rather than AI-fingerprint. v0.20: new rule.",
49989
50019
  aiSpecific: true,
49990
50020
  _v7Verdict: "DORMANT",
49991
50021
  _v7Lift: 1,
@@ -49994,6 +50024,11 @@ var init_signal_strength = __esm({
49994
50024
  _v7Precision: 0,
49995
50025
  _v8Verdict: "DORMANT",
49996
50026
  _v8Lift: 1,
50027
+ _v9Verdict: "DORMANT",
50028
+ _v9Lift: 0.27,
50029
+ _v9Recall: 0.1503,
50030
+ _v9FpRate: 0.2533,
50031
+ _v9Precision: 0.0695,
49997
50032
  defaultOff: true
49998
50033
  },
49999
50034
  "layout/forced-layout": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopbrick",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
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": {