slopbrick 0.17.3 → 0.17.4

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.
@@ -4084,6 +4084,9 @@ var VERDICTS = [
4084
4084
  "HYGIENE",
4085
4085
  "DORMANT"
4086
4086
  ];
4087
+ function isDefaultOff(verdict) {
4088
+ return verdict === "NOISY" || verdict === "INVERTED" || verdict === "DORMANT";
4089
+ }
4087
4090
  var signalStrengthSchema = external_exports.record(
4088
4091
  external_exports.string(),
4089
4092
  // ruleId
@@ -4364,7 +4367,11 @@ var FPR_FLOOR = 1e-6;
4364
4367
  var RECALL_FLOOR = 1e-6;
4365
4368
  var LLR_CAP = 13.8;
4366
4369
  var DEFAULT_PRIOR_PREVALENCE = 0.3;
4367
- var ELIGIBLE_VERDICTS = /* @__PURE__ */ new Set(["USEFUL", "OK"]);
4370
+ var ELIGIBLE_VERDICTS = new Set(
4371
+ ["USEFUL", "OK", "NOISY", "INVERTED", "HYGIENE", "DORMANT"].filter(
4372
+ (v) => v !== "HYGIENE" && !isDefaultOff(v)
4373
+ )
4374
+ );
4368
4375
  function loadSignals(signalData) {
4369
4376
  const map = /* @__PURE__ */ new Map();
4370
4377
  for (const [ruleId, entry] of Object.entries(signalData)) {
@@ -34909,7 +34916,7 @@ var mathElementUniformityRule = createRule({
34909
34916
  if (!anchor) anchor = { line: el.line, column: el.column };
34910
34917
  }
34911
34918
  }
34912
- const values = [counts.button, counts.input, counts.select].filter((v) => v > 0);
34919
+ const values = [counts.button, counts.input, counts.select].filter((v) => typeof v === "number" && v > 0);
34913
34920
  if (values.length < 2) return issues;
34914
34921
  const max = Math.max(...values);
34915
34922
  const min = Math.min(...values);
@@ -34958,7 +34965,7 @@ var mathGridUniformityRule = createRule({
34958
34965
  const { h, vocab, total } = shannonEntropy(counts);
34959
34966
  if (total < 4) return issues;
34960
34967
  if (h > 1) return issues;
34961
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
34968
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
34962
34969
  issues.push({
34963
34970
  ruleId: "layout/math-grid-uniformity",
34964
34971
  category: "layout",
@@ -35841,7 +35848,7 @@ var mathGiniClassUsageRule = createRule({
35841
35848
  if (g < 0.5) return issues;
35842
35849
  const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
35843
35850
  const topStr = sorted.map(([k, v]) => `${k}\xD7${v}`).join(", ");
35844
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
35851
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
35845
35852
  issues.push({
35846
35853
  ruleId: "logic/math-gini-class-usage",
35847
35854
  category: "logic",
@@ -37660,7 +37667,7 @@ function looksRealistic(value) {
37660
37667
  }
37661
37668
 
37662
37669
  // src/rules/test/missing-edge-case.ts
37663
- var import_core3 = require("@swc/core");
37670
+ var import_core4 = require("@swc/core");
37664
37671
  var import_node_fs11 = require("fs");
37665
37672
  var MAX_PER_FILE = 20;
37666
37673
  var missingEdgeCaseRule = createRule({
@@ -37704,7 +37711,7 @@ var missingEdgeCaseRule = createRule({
37704
37711
  if (!source) return issues;
37705
37712
  let ast;
37706
37713
  try {
37707
- ast = (0, import_core3.parseSync)(source, {
37714
+ ast = (0, import_core4.parseSync)(source, {
37708
37715
  syntax: "typescript",
37709
37716
  tsx: filePath.endsWith("tsx") || filePath.endsWith("jsx"),
37710
37717
  target: "es2022"
@@ -38821,7 +38828,7 @@ var mathFontEntropyRule = createRule({
38821
38828
  const { h, vocab, total } = shannonEntropy(counts);
38822
38829
  if (total < 6) return issues;
38823
38830
  if (h > 1.4) return issues;
38824
- const anchor = flatClassNames(facts.v2)[0];
38831
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
38825
38832
  issues.push({
38826
38833
  ruleId: "visual/math-font-entropy",
38827
38834
  category: "visual",
@@ -38943,7 +38950,7 @@ var mathRoundedEntropyRule = createRule({
38943
38950
  const { h, vocab, total } = shannonEntropy(counts);
38944
38951
  if (total < 6) return issues;
38945
38952
  if (h > 1.8) return issues;
38946
- const anchor = flatClassNames(facts.v2)[0];
38953
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
38947
38954
  issues.push({
38948
38955
  ruleId: "visual/math-rounded-entropy",
38949
38956
  category: "visual",
@@ -38982,7 +38989,7 @@ var mathSpacingEntropyRule = createRule({
38982
38989
  const { h, vocab, total } = shannonEntropy(counts);
38983
38990
  if (total < 10) return issues;
38984
38991
  if (h > 1.5) return issues;
38985
- const anchor = flatClassNames(facts.v2)[0];
38992
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
38986
38993
  issues.push({
38987
38994
  ruleId: "visual/math-spacing-entropy",
38988
38995
  category: "visual",
@@ -4065,6 +4065,9 @@ var VERDICTS = [
4065
4065
  "HYGIENE",
4066
4066
  "DORMANT"
4067
4067
  ];
4068
+ function isDefaultOff(verdict) {
4069
+ return verdict === "NOISY" || verdict === "INVERTED" || verdict === "DORMANT";
4070
+ }
4068
4071
  var signalStrengthSchema = external_exports.record(
4069
4072
  external_exports.string(),
4070
4073
  // ruleId
@@ -4345,7 +4348,11 @@ var FPR_FLOOR = 1e-6;
4345
4348
  var RECALL_FLOOR = 1e-6;
4346
4349
  var LLR_CAP = 13.8;
4347
4350
  var DEFAULT_PRIOR_PREVALENCE = 0.3;
4348
- var ELIGIBLE_VERDICTS = /* @__PURE__ */ new Set(["USEFUL", "OK"]);
4351
+ var ELIGIBLE_VERDICTS = new Set(
4352
+ ["USEFUL", "OK", "NOISY", "INVERTED", "HYGIENE", "DORMANT"].filter(
4353
+ (v) => v !== "HYGIENE" && !isDefaultOff(v)
4354
+ )
4355
+ );
4349
4356
  function loadSignals(signalData) {
4350
4357
  const map = /* @__PURE__ */ new Map();
4351
4358
  for (const [ruleId, entry] of Object.entries(signalData)) {
@@ -34890,7 +34897,7 @@ var mathElementUniformityRule = createRule({
34890
34897
  if (!anchor) anchor = { line: el.line, column: el.column };
34891
34898
  }
34892
34899
  }
34893
- const values = [counts.button, counts.input, counts.select].filter((v) => v > 0);
34900
+ const values = [counts.button, counts.input, counts.select].filter((v) => typeof v === "number" && v > 0);
34894
34901
  if (values.length < 2) return issues;
34895
34902
  const max = Math.max(...values);
34896
34903
  const min = Math.min(...values);
@@ -34939,7 +34946,7 @@ var mathGridUniformityRule = createRule({
34939
34946
  const { h, vocab, total } = shannonEntropy(counts);
34940
34947
  if (total < 4) return issues;
34941
34948
  if (h > 1) return issues;
34942
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
34949
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
34943
34950
  issues.push({
34944
34951
  ruleId: "layout/math-grid-uniformity",
34945
34952
  category: "layout",
@@ -35822,7 +35829,7 @@ var mathGiniClassUsageRule = createRule({
35822
35829
  if (g < 0.5) return issues;
35823
35830
  const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
35824
35831
  const topStr = sorted.map(([k, v]) => `${k}\xD7${v}`).join(", ");
35825
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
35832
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
35826
35833
  issues.push({
35827
35834
  ruleId: "logic/math-gini-class-usage",
35828
35835
  category: "logic",
@@ -38802,7 +38809,7 @@ var mathFontEntropyRule = createRule({
38802
38809
  const { h, vocab, total } = shannonEntropy(counts);
38803
38810
  if (total < 6) return issues;
38804
38811
  if (h > 1.4) return issues;
38805
- const anchor = flatClassNames(facts.v2)[0];
38812
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
38806
38813
  issues.push({
38807
38814
  ruleId: "visual/math-font-entropy",
38808
38815
  category: "visual",
@@ -38924,7 +38931,7 @@ var mathRoundedEntropyRule = createRule({
38924
38931
  const { h, vocab, total } = shannonEntropy(counts);
38925
38932
  if (total < 6) return issues;
38926
38933
  if (h > 1.8) return issues;
38927
- const anchor = flatClassNames(facts.v2)[0];
38934
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
38928
38935
  issues.push({
38929
38936
  ruleId: "visual/math-rounded-entropy",
38930
38937
  category: "visual",
@@ -38963,7 +38970,7 @@ var mathSpacingEntropyRule = createRule({
38963
38970
  const { h, vocab, total } = shannonEntropy(counts);
38964
38971
  if (total < 10) return issues;
38965
38972
  if (h > 1.5) return issues;
38966
- const anchor = flatClassNames(facts.v2)[0];
38973
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
38967
38974
  issues.push({
38968
38975
  ruleId: "visual/math-spacing-entropy",
38969
38976
  category: "visual",
package/dist/index.cjs CHANGED
@@ -30242,7 +30242,7 @@ var init_math_element_uniformity = __esm({
30242
30242
  if (!anchor) anchor = { line: el.line, column: el.column };
30243
30243
  }
30244
30244
  }
30245
- const values = [counts.button, counts.input, counts.select].filter((v) => v > 0);
30245
+ const values = [counts.button, counts.input, counts.select].filter((v) => typeof v === "number" && v > 0);
30246
30246
  if (values.length < 2) return issues;
30247
30247
  const max = Math.max(...values);
30248
30248
  const min = Math.min(...values);
@@ -30300,7 +30300,7 @@ var init_math_grid_uniformity = __esm({
30300
30300
  const { h, vocab, total } = shannonEntropy(counts);
30301
30301
  if (total < 4) return issues;
30302
30302
  if (h > 1) return issues;
30303
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
30303
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
30304
30304
  issues.push({
30305
30305
  ruleId: "layout/math-grid-uniformity",
30306
30306
  category: "layout",
@@ -34733,59 +34733,77 @@ __export(dist_exports, {
34733
34733
  writeCacheFromInventory: () => writeCacheFromInventory,
34734
34734
  writeJsonAtomic: () => writeJsonAtomic
34735
34735
  });
34736
+ function isRecord(v) {
34737
+ return typeof v === "object" && v !== null && !Array.isArray(v);
34738
+ }
34739
+ function isStringArray(v) {
34740
+ return Array.isArray(v) && v.every((i) => typeof i === "string");
34741
+ }
34742
+ function isNumber(v) {
34743
+ return typeof v === "number" && Number.isFinite(v);
34744
+ }
34736
34745
  function isStructurePattern(value) {
34737
- if (typeof value !== "object" || value === null) return false;
34738
- const v = value;
34739
- return typeof v.category === "string" && typeof v.name === "string" && Array.isArray(v.imports) && v.imports.every((i) => typeof i === "string") && typeof v.fileCount === "number";
34746
+ if (!isRecord(value)) return false;
34747
+ return typeof value.category === "string" && typeof value.name === "string" && isStringArray(value.imports) && isNumber(value.fileCount);
34740
34748
  }
34741
34749
  function isComponentFingerprint(value) {
34742
- if (typeof value !== "object" || value === null) return false;
34743
- const v = value;
34744
- return typeof v.name === "string" && Array.isArray(v.files) && v.files.every((f) => typeof f === "string") && typeof v.fingerprint === "string" && Array.isArray(v.hooks) && v.hooks.every((h) => typeof h === "string") && Array.isArray(v.props) && v.props.every((p) => typeof p === "string") && typeof v.line === "number" && typeof v.endLine === "number";
34750
+ if (!isRecord(value)) return false;
34751
+ return typeof value.name === "string" && isStringArray(value.files) && typeof value.fingerprint === "string" && isStringArray(value.hooks) && isStringArray(value.props) && isNumber(value.line) && isNumber(value.endLine);
34752
+ }
34753
+ function isVersion3(value) {
34754
+ return value === STRUCTURE_SCHEMA_VERSION;
34745
34755
  }
34746
34756
  function isInventoryFile(value) {
34747
- if (typeof value !== "object" || value === null) return false;
34748
- const v = value;
34749
- return v.version === STRUCTURE_SCHEMA_VERSION && typeof v.generatedAt === "string" && typeof v.workspace === "string" && typeof v.scannedFiles === "number" && typeof v.scanDurationMs === "number" && Array.isArray(v.patterns) && Array.isArray(v.components) && v.patterns.every(isStructurePattern) && v.components.every(isComponentFingerprint);
34757
+ if (!isRecord(value)) return false;
34758
+ if (!isVersion3(value.version)) return false;
34759
+ if (typeof value.generatedAt !== "string") return false;
34760
+ if (typeof value.workspace !== "string") return false;
34761
+ if (!isNumber(value.scannedFiles)) return false;
34762
+ if (!isNumber(value.scanDurationMs)) return false;
34763
+ if (!Array.isArray(value.patterns) || !value.patterns.every(isStructurePattern)) return false;
34764
+ if (!Array.isArray(value.components) || !value.components.every(isComponentFingerprint)) return false;
34765
+ return true;
34750
34766
  }
34751
34767
  function isConstitutionFile(value) {
34752
- if (typeof value !== "object" || value === null) return false;
34753
- const v = value;
34754
- return v.version === STRUCTURE_SCHEMA_VERSION && typeof v.generatedAt === "string" && typeof v.workspace === "string" && typeof v.declared === "object" && v.declared !== null && Array.isArray(v.forbidden) && v.forbidden.every((f) => typeof f === "string") && Array.isArray(v.forbiddenPrefixes) && v.forbiddenPrefixes.every((f) => typeof f === "string");
34768
+ if (!isRecord(value)) return false;
34769
+ if (!isVersion3(value.version)) return false;
34770
+ if (typeof value.generatedAt !== "string") return false;
34771
+ if (typeof value.workspace !== "string") return false;
34772
+ if (!isRecord(value.declared)) return false;
34773
+ if (!isStringArray(value.forbidden)) return false;
34774
+ if (!isStringArray(value.forbiddenPrefixes)) return false;
34775
+ return true;
34755
34776
  }
34756
34777
  function isFileMtimeEntry(value) {
34757
- if (typeof value !== "object" || value === null) return false;
34758
- const v = value;
34759
- return typeof v.file === "string" && typeof v.mtimeMs === "number" && typeof v.hash === "string";
34778
+ if (!isRecord(value)) return false;
34779
+ return typeof value.file === "string" && isNumber(value.mtimeMs) && typeof value.hash === "string";
34760
34780
  }
34761
34781
  function isHealthFile(value) {
34762
- if (typeof value !== "object" || value === null) return false;
34763
- const v = value;
34764
- if (v.version !== STRUCTURE_SCHEMA_VERSION) return false;
34765
- if (typeof v.generatedAt !== "string") return false;
34766
- if (typeof v.workspace !== "string") return false;
34767
- if (typeof v.aiQuality !== "number") return false;
34768
- if (typeof v.engineeringHygiene !== "number") return false;
34769
- if (typeof v.security !== "number") return false;
34770
- if (typeof v.repositoryHealth !== "number") return false;
34771
- if (typeof v.issueCounts !== "object" || v.issueCounts === null) return false;
34772
- const counts = v.issueCounts;
34773
- if (typeof counts.high !== "number") return false;
34774
- if (typeof counts.medium !== "number") return false;
34775
- if (typeof counts.low !== "number") return false;
34776
- if (v.slopIndex !== void 0 && typeof v.slopIndex !== "number") return false;
34777
- if (v.categoryScores !== void 0) {
34778
- if (typeof v.categoryScores !== "object" || v.categoryScores === null) return false;
34779
- for (const score of Object.values(v.categoryScores)) {
34780
- if (typeof score !== "number") return false;
34781
- }
34782
- }
34783
- if (v.constitutionDrift !== void 0 && typeof v.constitutionDrift !== "number") return false;
34784
- if (v.topOffenseIds !== void 0) {
34785
- if (!Array.isArray(v.topOffenseIds)) return false;
34786
- if (!v.topOffenseIds.every((id) => typeof id === "string")) return false;
34787
- }
34788
- if (v.scanDurationMs !== void 0 && typeof v.scanDurationMs !== "number") return false;
34782
+ if (!isRecord(value)) return false;
34783
+ if (!isVersion3(value.version)) return false;
34784
+ if (typeof value.generatedAt !== "string") return false;
34785
+ if (typeof value.workspace !== "string") return false;
34786
+ if (!isNumber(value.aiQuality)) return false;
34787
+ if (!isNumber(value.engineeringHygiene)) return false;
34788
+ if (!isNumber(value.security)) return false;
34789
+ if (!isNumber(value.repositoryHealth)) return false;
34790
+ if (!isRecord(value.issueCounts)) return false;
34791
+ const counts = value.issueCounts;
34792
+ if (!isNumber(counts.high)) return false;
34793
+ if (!isNumber(counts.medium)) return false;
34794
+ if (!isNumber(counts.low)) return false;
34795
+ if (value.slopIndex !== void 0 && !isNumber(value.slopIndex)) return false;
34796
+ if (value.categoryScores !== void 0) {
34797
+ if (!isRecord(value.categoryScores)) return false;
34798
+ for (const score of Object.values(value.categoryScores)) {
34799
+ if (!isNumber(score)) return false;
34800
+ }
34801
+ }
34802
+ if (value.constitutionDrift !== void 0 && !isNumber(value.constitutionDrift)) return false;
34803
+ if (value.topOffenseIds !== void 0) {
34804
+ if (!isStringArray(value.topOffenseIds)) return false;
34805
+ }
34806
+ if (value.scanDurationMs !== void 0 && !isNumber(value.scanDurationMs)) return false;
34789
34807
  return true;
34790
34808
  }
34791
34809
  function inventoryPath(workspaceDir) {
@@ -36801,6 +36819,7 @@ var init_dist2 = __esm({
36801
36819
  import_path3 = require("path");
36802
36820
  import_crypto2 = require("crypto");
36803
36821
  init_dist();
36822
+ init_dist();
36804
36823
  import_crypto3 = require("crypto");
36805
36824
  import_promises2 = require("fs/promises");
36806
36825
  import_path4 = require("path");
@@ -36812,7 +36831,11 @@ var init_dist2 = __esm({
36812
36831
  RECALL_FLOOR = 1e-6;
36813
36832
  LLR_CAP = 13.8;
36814
36833
  DEFAULT_PRIOR_PREVALENCE = 0.3;
36815
- ELIGIBLE_VERDICTS = /* @__PURE__ */ new Set(["USEFUL", "OK"]);
36834
+ ELIGIBLE_VERDICTS = new Set(
36835
+ ["USEFUL", "OK", "NOISY", "INVERTED", "HYGIENE", "DORMANT"].filter(
36836
+ (v) => v !== "HYGIENE" && !isDefaultOff(v)
36837
+ )
36838
+ );
36816
36839
  SUFFIXES_TO_STRIP = [
36817
36840
  "RepositoryClient",
36818
36841
  "ServiceFactory",
@@ -37652,7 +37675,7 @@ var init_math_gini_class_usage = __esm({
37652
37675
  if (g < 0.5) return issues;
37653
37676
  const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
37654
37677
  const topStr = sorted.map(([k, v]) => `${k}\xD7${v}`).join(", ");
37655
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
37678
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
37656
37679
  issues.push({
37657
37680
  ruleId: "logic/math-gini-class-usage",
37658
37681
  category: "logic",
@@ -39912,11 +39935,11 @@ function coveredFunctionNames(names, testFileSources, cwd) {
39912
39935
  }
39913
39936
  return covered;
39914
39937
  }
39915
- var import_core3, import_node_fs10, MAX_PER_FILE, missingEdgeCaseRule;
39938
+ var import_core4, import_node_fs10, MAX_PER_FILE, missingEdgeCaseRule;
39916
39939
  var init_missing_edge_case = __esm({
39917
39940
  "src/rules/test/missing-edge-case.ts"() {
39918
39941
  "use strict";
39919
- import_core3 = require("@swc/core");
39942
+ import_core4 = require("@swc/core");
39920
39943
  import_node_fs10 = require("fs");
39921
39944
  init_rule();
39922
39945
  MAX_PER_FILE = 20;
@@ -39961,7 +39984,7 @@ var init_missing_edge_case = __esm({
39961
39984
  if (!source) return issues;
39962
39985
  let ast;
39963
39986
  try {
39964
- ast = (0, import_core3.parseSync)(source, {
39987
+ ast = (0, import_core4.parseSync)(source, {
39965
39988
  syntax: "typescript",
39966
39989
  tsx: filePath.endsWith("tsx") || filePath.endsWith("jsx"),
39967
39990
  target: "es2022"
@@ -40955,7 +40978,7 @@ var init_math_font_entropy = __esm({
40955
40978
  const { h, vocab, total } = shannonEntropy(counts);
40956
40979
  if (total < 6) return issues;
40957
40980
  if (h > 1.4) return issues;
40958
- const anchor = flatClassNames(facts.v2)[0];
40981
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
40959
40982
  issues.push({
40960
40983
  ruleId: "visual/math-font-entropy",
40961
40984
  category: "visual",
@@ -41095,7 +41118,7 @@ var init_math_rounded_entropy = __esm({
41095
41118
  const { h, vocab, total } = shannonEntropy(counts);
41096
41119
  if (total < 6) return issues;
41097
41120
  if (h > 1.8) return issues;
41098
- const anchor = flatClassNames(facts.v2)[0];
41121
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
41099
41122
  issues.push({
41100
41123
  ruleId: "visual/math-rounded-entropy",
41101
41124
  category: "visual",
@@ -41143,7 +41166,7 @@ var init_math_spacing_entropy = __esm({
41143
41166
  const { h, vocab, total } = shannonEntropy(counts);
41144
41167
  if (total < 10) return issues;
41145
41168
  if (h > 1.5) return issues;
41146
- const anchor = flatClassNames(facts.v2)[0];
41169
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
41147
41170
  issues.push({
41148
41171
  ruleId: "visual/math-spacing-entropy",
41149
41172
  category: "visual",
@@ -48206,7 +48229,7 @@ function confirmMagicRateLiteral(line) {
48206
48229
  const probe = `let __probe = (${line});`;
48207
48230
  let ast;
48208
48231
  try {
48209
- ast = (0, import_core5.parseSync)(probe, { syntax: "ecmascript", target: "es2022" });
48232
+ ast = (0, import_core6.parseSync)(probe, { syntax: "ecmascript", target: "es2022" });
48210
48233
  } catch {
48211
48234
  return true;
48212
48235
  }
@@ -48485,11 +48508,11 @@ function extractIdentifier(line) {
48485
48508
  const m = line.match(/Math\.round\s*\(\s*([A-Za-z_$][\w$.]*)/);
48486
48509
  return m ? m[1] ?? "" : "<expr>";
48487
48510
  }
48488
- var import_core5, BUSINESS_LOGIC_WEIGHTS, CURRENCY_IDENTIFIERS, COMMON_RATE_LITERALS, MATH_ROUND_CENTS, MAGIC_RATE_DECIMAL, HARDCODED_CURRENCY_SYMBOL, SOURCE_EXTENSION, ZOD_STRING, ZOD_STRING_CALL, REQUIRED_ERROR, INVALID_TYPE_ERROR, HARDCODED_ISO_DATE, LOCALE_STRING_NO_OPTIONS, RAW_CURRENCY_IN_TEMPLATE;
48511
+ var import_core6, BUSINESS_LOGIC_WEIGHTS, CURRENCY_IDENTIFIERS, COMMON_RATE_LITERALS, MATH_ROUND_CENTS, MAGIC_RATE_DECIMAL, HARDCODED_CURRENCY_SYMBOL, SOURCE_EXTENSION, ZOD_STRING, ZOD_STRING_CALL, REQUIRED_ERROR, INVALID_TYPE_ERROR, HARDCODED_ISO_DATE, LOCALE_STRING_NO_OPTIONS, RAW_CURRENCY_IN_TEMPLATE;
48489
48512
  var init_business_logic = __esm({
48490
48513
  "src/engine/business-logic.ts"() {
48491
48514
  "use strict";
48492
- import_core5 = require("@swc/core");
48515
+ import_core6 = require("@swc/core");
48493
48516
  BUSINESS_LOGIC_WEIGHTS = {
48494
48517
  pricing: 3,
48495
48518
  validation: 2,
@@ -49860,6 +49883,13 @@ function mergeComponentsByName(components) {
49860
49883
  }
49861
49884
  byName.set(c.name, {
49862
49885
  ...c,
49886
+ // All visitors (rust.ts, php.ts, go.ts, …) push Components with
49887
+ // `files: [filePath]` — non-empty by construction. The JSON
49888
+ // Schema (inventory.schema.json) requires `files` to be
49889
+ // `[string, ...string[]]` (at least 1); the cast is safe under
49890
+ // the visitor invariant. If a future visitor ever produces an
49891
+ // empty files array, the runtime validator
49892
+ // (`isInventoryFile`) will reject the artifact at write time.
49863
49893
  files: c.files.slice(),
49864
49894
  hooks: c.hooks.slice(),
49865
49895
  props: c.props.slice()
@@ -55053,7 +55083,7 @@ function promptMultiSelect(rl, question, options, defaultValue) {
55053
55083
  resolve18([]);
55054
55084
  return;
55055
55085
  }
55056
- const selected = [...new Set(numbers.map((n) => options[n - 2]))];
55086
+ const selected = [...new Set(numbers.map((n) => options[n - 2]).filter((v) => v !== void 0))];
55057
55087
  resolve18(selected);
55058
55088
  });
55059
55089
  }
package/dist/index.js CHANGED
@@ -30235,7 +30235,7 @@ var init_math_element_uniformity = __esm({
30235
30235
  if (!anchor) anchor = { line: el.line, column: el.column };
30236
30236
  }
30237
30237
  }
30238
- const values = [counts.button, counts.input, counts.select].filter((v) => v > 0);
30238
+ const values = [counts.button, counts.input, counts.select].filter((v) => typeof v === "number" && v > 0);
30239
30239
  if (values.length < 2) return issues;
30240
30240
  const max = Math.max(...values);
30241
30241
  const min = Math.min(...values);
@@ -30293,7 +30293,7 @@ var init_math_grid_uniformity = __esm({
30293
30293
  const { h, vocab, total } = shannonEntropy(counts);
30294
30294
  if (total < 4) return issues;
30295
30295
  if (h > 1) return issues;
30296
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
30296
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
30297
30297
  issues.push({
30298
30298
  ruleId: "layout/math-grid-uniformity",
30299
30299
  category: "layout",
@@ -34728,59 +34728,77 @@ __export(dist_exports, {
34728
34728
  });
34729
34729
  import { writeFileSync, renameSync, existsSync as existsSync8, readFileSync as readFileSync9, statSync as statSync4, mkdirSync } from "fs";
34730
34730
  import { join as join9, dirname as dirname5 } from "path";
34731
+ function isRecord(v) {
34732
+ return typeof v === "object" && v !== null && !Array.isArray(v);
34733
+ }
34734
+ function isStringArray(v) {
34735
+ return Array.isArray(v) && v.every((i) => typeof i === "string");
34736
+ }
34737
+ function isNumber(v) {
34738
+ return typeof v === "number" && Number.isFinite(v);
34739
+ }
34731
34740
  function isStructurePattern(value) {
34732
- if (typeof value !== "object" || value === null) return false;
34733
- const v = value;
34734
- return typeof v.category === "string" && typeof v.name === "string" && Array.isArray(v.imports) && v.imports.every((i) => typeof i === "string") && typeof v.fileCount === "number";
34741
+ if (!isRecord(value)) return false;
34742
+ return typeof value.category === "string" && typeof value.name === "string" && isStringArray(value.imports) && isNumber(value.fileCount);
34735
34743
  }
34736
34744
  function isComponentFingerprint(value) {
34737
- if (typeof value !== "object" || value === null) return false;
34738
- const v = value;
34739
- return typeof v.name === "string" && Array.isArray(v.files) && v.files.every((f) => typeof f === "string") && typeof v.fingerprint === "string" && Array.isArray(v.hooks) && v.hooks.every((h) => typeof h === "string") && Array.isArray(v.props) && v.props.every((p) => typeof p === "string") && typeof v.line === "number" && typeof v.endLine === "number";
34745
+ if (!isRecord(value)) return false;
34746
+ return typeof value.name === "string" && isStringArray(value.files) && typeof value.fingerprint === "string" && isStringArray(value.hooks) && isStringArray(value.props) && isNumber(value.line) && isNumber(value.endLine);
34747
+ }
34748
+ function isVersion3(value) {
34749
+ return value === STRUCTURE_SCHEMA_VERSION;
34740
34750
  }
34741
34751
  function isInventoryFile(value) {
34742
- if (typeof value !== "object" || value === null) return false;
34743
- const v = value;
34744
- return v.version === STRUCTURE_SCHEMA_VERSION && typeof v.generatedAt === "string" && typeof v.workspace === "string" && typeof v.scannedFiles === "number" && typeof v.scanDurationMs === "number" && Array.isArray(v.patterns) && Array.isArray(v.components) && v.patterns.every(isStructurePattern) && v.components.every(isComponentFingerprint);
34752
+ if (!isRecord(value)) return false;
34753
+ if (!isVersion3(value.version)) return false;
34754
+ if (typeof value.generatedAt !== "string") return false;
34755
+ if (typeof value.workspace !== "string") return false;
34756
+ if (!isNumber(value.scannedFiles)) return false;
34757
+ if (!isNumber(value.scanDurationMs)) return false;
34758
+ if (!Array.isArray(value.patterns) || !value.patterns.every(isStructurePattern)) return false;
34759
+ if (!Array.isArray(value.components) || !value.components.every(isComponentFingerprint)) return false;
34760
+ return true;
34745
34761
  }
34746
34762
  function isConstitutionFile(value) {
34747
- if (typeof value !== "object" || value === null) return false;
34748
- const v = value;
34749
- return v.version === STRUCTURE_SCHEMA_VERSION && typeof v.generatedAt === "string" && typeof v.workspace === "string" && typeof v.declared === "object" && v.declared !== null && Array.isArray(v.forbidden) && v.forbidden.every((f) => typeof f === "string") && Array.isArray(v.forbiddenPrefixes) && v.forbiddenPrefixes.every((f) => typeof f === "string");
34763
+ if (!isRecord(value)) return false;
34764
+ if (!isVersion3(value.version)) return false;
34765
+ if (typeof value.generatedAt !== "string") return false;
34766
+ if (typeof value.workspace !== "string") return false;
34767
+ if (!isRecord(value.declared)) return false;
34768
+ if (!isStringArray(value.forbidden)) return false;
34769
+ if (!isStringArray(value.forbiddenPrefixes)) return false;
34770
+ return true;
34750
34771
  }
34751
34772
  function isFileMtimeEntry(value) {
34752
- if (typeof value !== "object" || value === null) return false;
34753
- const v = value;
34754
- return typeof v.file === "string" && typeof v.mtimeMs === "number" && typeof v.hash === "string";
34773
+ if (!isRecord(value)) return false;
34774
+ return typeof value.file === "string" && isNumber(value.mtimeMs) && typeof value.hash === "string";
34755
34775
  }
34756
34776
  function isHealthFile(value) {
34757
- if (typeof value !== "object" || value === null) return false;
34758
- const v = value;
34759
- if (v.version !== STRUCTURE_SCHEMA_VERSION) return false;
34760
- if (typeof v.generatedAt !== "string") return false;
34761
- if (typeof v.workspace !== "string") return false;
34762
- if (typeof v.aiQuality !== "number") return false;
34763
- if (typeof v.engineeringHygiene !== "number") return false;
34764
- if (typeof v.security !== "number") return false;
34765
- if (typeof v.repositoryHealth !== "number") return false;
34766
- if (typeof v.issueCounts !== "object" || v.issueCounts === null) return false;
34767
- const counts = v.issueCounts;
34768
- if (typeof counts.high !== "number") return false;
34769
- if (typeof counts.medium !== "number") return false;
34770
- if (typeof counts.low !== "number") return false;
34771
- if (v.slopIndex !== void 0 && typeof v.slopIndex !== "number") return false;
34772
- if (v.categoryScores !== void 0) {
34773
- if (typeof v.categoryScores !== "object" || v.categoryScores === null) return false;
34774
- for (const score of Object.values(v.categoryScores)) {
34775
- if (typeof score !== "number") return false;
34776
- }
34777
- }
34778
- if (v.constitutionDrift !== void 0 && typeof v.constitutionDrift !== "number") return false;
34779
- if (v.topOffenseIds !== void 0) {
34780
- if (!Array.isArray(v.topOffenseIds)) return false;
34781
- if (!v.topOffenseIds.every((id) => typeof id === "string")) return false;
34782
- }
34783
- if (v.scanDurationMs !== void 0 && typeof v.scanDurationMs !== "number") return false;
34777
+ if (!isRecord(value)) return false;
34778
+ if (!isVersion3(value.version)) return false;
34779
+ if (typeof value.generatedAt !== "string") return false;
34780
+ if (typeof value.workspace !== "string") return false;
34781
+ if (!isNumber(value.aiQuality)) return false;
34782
+ if (!isNumber(value.engineeringHygiene)) return false;
34783
+ if (!isNumber(value.security)) return false;
34784
+ if (!isNumber(value.repositoryHealth)) return false;
34785
+ if (!isRecord(value.issueCounts)) return false;
34786
+ const counts = value.issueCounts;
34787
+ if (!isNumber(counts.high)) return false;
34788
+ if (!isNumber(counts.medium)) return false;
34789
+ if (!isNumber(counts.low)) return false;
34790
+ if (value.slopIndex !== void 0 && !isNumber(value.slopIndex)) return false;
34791
+ if (value.categoryScores !== void 0) {
34792
+ if (!isRecord(value.categoryScores)) return false;
34793
+ for (const score of Object.values(value.categoryScores)) {
34794
+ if (!isNumber(score)) return false;
34795
+ }
34796
+ }
34797
+ if (value.constitutionDrift !== void 0 && !isNumber(value.constitutionDrift)) return false;
34798
+ if (value.topOffenseIds !== void 0) {
34799
+ if (!isStringArray(value.topOffenseIds)) return false;
34800
+ }
34801
+ if (value.scanDurationMs !== void 0 && !isNumber(value.scanDurationMs)) return false;
34784
34802
  return true;
34785
34803
  }
34786
34804
  function inventoryPath(workspaceDir) {
@@ -36797,6 +36815,7 @@ var init_dist2 = __esm({
36797
36815
  "../engine/dist/index.js"() {
36798
36816
  "use strict";
36799
36817
  init_dist();
36818
+ init_dist();
36800
36819
  SMOOTHING = 0.5;
36801
36820
  DEFAULT_PRIOR = { pAI: 0.5, pHuman: 0.5 };
36802
36821
  TELEMETRY_FILE = join22(".slopbrick", "structure.json");
@@ -36805,7 +36824,11 @@ var init_dist2 = __esm({
36805
36824
  RECALL_FLOOR = 1e-6;
36806
36825
  LLR_CAP = 13.8;
36807
36826
  DEFAULT_PRIOR_PREVALENCE = 0.3;
36808
- ELIGIBLE_VERDICTS = /* @__PURE__ */ new Set(["USEFUL", "OK"]);
36827
+ ELIGIBLE_VERDICTS = new Set(
36828
+ ["USEFUL", "OK", "NOISY", "INVERTED", "HYGIENE", "DORMANT"].filter(
36829
+ (v) => v !== "HYGIENE" && !isDefaultOff(v)
36830
+ )
36831
+ );
36809
36832
  SUFFIXES_TO_STRIP = [
36810
36833
  "RepositoryClient",
36811
36834
  "ServiceFactory",
@@ -37645,7 +37668,7 @@ var init_math_gini_class_usage = __esm({
37645
37668
  if (g < 0.5) return issues;
37646
37669
  const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
37647
37670
  const topStr = sorted.map(([k, v]) => `${k}\xD7${v}`).join(", ");
37648
- const anchor = facts.v2 ? flatClassNames(facts.v2)[0] : { line: 1, column: 1 };
37671
+ const anchor = facts.v2 ? flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 } : { line: 1, column: 1 };
37649
37672
  issues.push({
37650
37673
  ruleId: "logic/math-gini-class-usage",
37651
37674
  category: "logic",
@@ -40948,7 +40971,7 @@ var init_math_font_entropy = __esm({
40948
40971
  const { h, vocab, total } = shannonEntropy(counts);
40949
40972
  if (total < 6) return issues;
40950
40973
  if (h > 1.4) return issues;
40951
- const anchor = flatClassNames(facts.v2)[0];
40974
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
40952
40975
  issues.push({
40953
40976
  ruleId: "visual/math-font-entropy",
40954
40977
  category: "visual",
@@ -41088,7 +41111,7 @@ var init_math_rounded_entropy = __esm({
41088
41111
  const { h, vocab, total } = shannonEntropy(counts);
41089
41112
  if (total < 6) return issues;
41090
41113
  if (h > 1.8) return issues;
41091
- const anchor = flatClassNames(facts.v2)[0];
41114
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
41092
41115
  issues.push({
41093
41116
  ruleId: "visual/math-rounded-entropy",
41094
41117
  category: "visual",
@@ -41136,7 +41159,7 @@ var init_math_spacing_entropy = __esm({
41136
41159
  const { h, vocab, total } = shannonEntropy(counts);
41137
41160
  if (total < 10) return issues;
41138
41161
  if (h > 1.5) return issues;
41139
- const anchor = flatClassNames(facts.v2)[0];
41162
+ const anchor = flatClassNames(facts.v2)[0] ?? { line: 1, column: 1 };
41140
41163
  issues.push({
41141
41164
  ruleId: "visual/math-spacing-entropy",
41142
41165
  category: "visual",
@@ -49859,6 +49882,13 @@ function mergeComponentsByName(components) {
49859
49882
  }
49860
49883
  byName.set(c.name, {
49861
49884
  ...c,
49885
+ // All visitors (rust.ts, php.ts, go.ts, …) push Components with
49886
+ // `files: [filePath]` — non-empty by construction. The JSON
49887
+ // Schema (inventory.schema.json) requires `files` to be
49888
+ // `[string, ...string[]]` (at least 1); the cast is safe under
49889
+ // the visitor invariant. If a future visitor ever produces an
49890
+ // empty files array, the runtime validator
49891
+ // (`isInventoryFile`) will reject the artifact at write time.
49862
49892
  files: c.files.slice(),
49863
49893
  hooks: c.hooks.slice(),
49864
49894
  props: c.props.slice()
@@ -54972,7 +55002,7 @@ function promptMultiSelect(rl, question, options, defaultValue) {
54972
55002
  resolve18([]);
54973
55003
  return;
54974
55004
  }
54975
- const selected = [...new Set(numbers.map((n) => options[n - 2]))];
55005
+ const selected = [...new Set(numbers.map((n) => options[n - 2]).filter((v) => v !== void 0))];
54976
55006
  resolve18(selected);
54977
55007
  });
54978
55008
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopbrick",
3
- "version": "0.17.3",
3
+ "version": "0.17.4",
4
4
  "description": "Discovered, modeled, and governed repository structure. SlopBrick scans source code, classifies it against 95 rules in 15 categories, computes 4 scores (aiQuality, engineeringHygiene, security, repositoryHealth), and persists the structure for AI agents and CI.",
5
5
  "type": "module",
6
6
  "bin": {