slopbrick 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +45 -4
- package/dist/index.d.cts +63 -1
- package/dist/index.d.ts +63 -1
- package/dist/index.js +45 -4
- package/package.json +1 -1
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.
|
|
39
|
+
VERSION = "0.40.0";
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -50764,6 +50764,12 @@ function severityBump(severity) {
|
|
|
50764
50764
|
const idx = order.indexOf(severity);
|
|
50765
50765
|
return order[Math.min(idx + 1, order.length - 1)];
|
|
50766
50766
|
}
|
|
50767
|
+
function severityRelax(severity) {
|
|
50768
|
+
const order = ["off", "low", "medium", "high"];
|
|
50769
|
+
const idx = order.indexOf(severity);
|
|
50770
|
+
if (idx <= 0) return "off";
|
|
50771
|
+
return order[idx - 1];
|
|
50772
|
+
}
|
|
50767
50773
|
function resolveEffectiveSeverity(override, defaultSeverity) {
|
|
50768
50774
|
if (override === void 0 || override === "off" || override === "auto") {
|
|
50769
50775
|
return defaultSeverity;
|
|
@@ -50794,6 +50800,7 @@ function countConsecutiveTopFileAppearances(currentHash, recentTopHashes) {
|
|
|
50794
50800
|
}
|
|
50795
50801
|
function computeFlywheelOutput(runs, currentTopFiles, recentTopHashes, unmatchedStringLiterals, config, rules) {
|
|
50796
50802
|
const autoTuned = [];
|
|
50803
|
+
const autoRelaxed = [];
|
|
50797
50804
|
const hotspotIssues = [];
|
|
50798
50805
|
const suggestions = [];
|
|
50799
50806
|
const ruleById = new Map(rules.map((r) => [r.id, r]));
|
|
@@ -50814,6 +50821,29 @@ function computeFlywheelOutput(runs, currentTopFiles, recentTopHashes, unmatched
|
|
|
50814
50821
|
}
|
|
50815
50822
|
}
|
|
50816
50823
|
}
|
|
50824
|
+
if (runs.length >= IGNORE_THRESHOLD) {
|
|
50825
|
+
const window = runs.slice(-IGNORE_THRESHOLD);
|
|
50826
|
+
const candidateRuleIds = new Set(window.flatMap((r) => r.topOffenseIds));
|
|
50827
|
+
for (const ruleId of candidateRuleIds) {
|
|
50828
|
+
const rule = ruleById.get(ruleId);
|
|
50829
|
+
const defaultSeverity = rule?.severity ?? "medium";
|
|
50830
|
+
const currentSeverity = resolveEffectiveSeverity(
|
|
50831
|
+
config.rules[ruleId] ?? "auto",
|
|
50832
|
+
defaultSeverity
|
|
50833
|
+
);
|
|
50834
|
+
const streak = countConsecutiveTopOffenses(runs, ruleId);
|
|
50835
|
+
if (streak >= IGNORE_THRESHOLD) {
|
|
50836
|
+
autoRelaxed.push({
|
|
50837
|
+
ruleId,
|
|
50838
|
+
severity: severityRelax(currentSeverity),
|
|
50839
|
+
previousSeverity: currentSeverity,
|
|
50840
|
+
reason: `In top-3 offenses for ${streak} consecutive scans; corpus prior ${DEFAULT_RELAXATION_PRIOR.toFixed(2)} suggests the rule is high-FP for this corpus slice.`,
|
|
50841
|
+
defaultPrior: DEFAULT_RELAXATION_PRIOR,
|
|
50842
|
+
relaxedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
50843
|
+
});
|
|
50844
|
+
}
|
|
50845
|
+
}
|
|
50846
|
+
}
|
|
50817
50847
|
if (currentTopFiles.length > 0 && runs.length >= CONSECUTIVE_THRESHOLD) {
|
|
50818
50848
|
for (const file of currentTopFiles) {
|
|
50819
50849
|
const consecutive = countConsecutiveTopFileAppearances(file.hash, recentTopHashes);
|
|
@@ -50848,13 +50878,14 @@ function computeFlywheelOutput(runs, currentTopFiles, recentTopHashes, unmatched
|
|
|
50848
50878
|
});
|
|
50849
50879
|
}
|
|
50850
50880
|
}
|
|
50851
|
-
return { autoTuned, hotspotIssues, suggestions };
|
|
50881
|
+
return { autoTuned, autoRelaxed, hotspotIssues, suggestions };
|
|
50852
50882
|
}
|
|
50853
50883
|
function migrateFlywheelState(state) {
|
|
50854
50884
|
return {
|
|
50855
50885
|
version: FLYWHEEL_VERSION,
|
|
50856
50886
|
updatedAt: state.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
50857
50887
|
autoTuned: state.autoTuned ?? [],
|
|
50888
|
+
autoRelaxed: state.autoRelaxed ?? [],
|
|
50858
50889
|
research: state.research
|
|
50859
50890
|
};
|
|
50860
50891
|
}
|
|
@@ -50910,7 +50941,7 @@ function saveFlywheelState(cwd, state) {
|
|
|
50910
50941
|
function hashFile(filePath) {
|
|
50911
50942
|
return (0, import_node_crypto6.createHash)("sha256").update(filePath).digest("hex").slice(0, 16);
|
|
50912
50943
|
}
|
|
50913
|
-
var import_node_crypto6, import_node_fs14, import_node_path15, FLYWHEEL_DIR, STATE_FILE, CONSECUTIVE_THRESHOLD, FLYWHEEL_VERSION;
|
|
50944
|
+
var import_node_crypto6, import_node_fs14, import_node_path15, FLYWHEEL_DIR, STATE_FILE, CONSECUTIVE_THRESHOLD, IGNORE_THRESHOLD, DEFAULT_RELAXATION_PRIOR, FLYWHEEL_VERSION;
|
|
50914
50945
|
var init_flywheel = __esm({
|
|
50915
50946
|
"src/engine/flywheel.ts"() {
|
|
50916
50947
|
"use strict";
|
|
@@ -50920,7 +50951,9 @@ var init_flywheel = __esm({
|
|
|
50920
50951
|
FLYWHEEL_DIR = ".slopbrick/flywheel";
|
|
50921
50952
|
STATE_FILE = "auto-tuned.json";
|
|
50922
50953
|
CONSECUTIVE_THRESHOLD = 3;
|
|
50923
|
-
|
|
50954
|
+
IGNORE_THRESHOLD = 5;
|
|
50955
|
+
DEFAULT_RELAXATION_PRIOR = 0.3;
|
|
50956
|
+
FLYWHEEL_VERSION = "3";
|
|
50924
50957
|
}
|
|
50925
50958
|
});
|
|
50926
50959
|
|
|
@@ -54623,6 +54656,7 @@ async function persistRun(input) {
|
|
|
54623
54656
|
);
|
|
54624
54657
|
const state = loadFlywheelState(cwd);
|
|
54625
54658
|
state.autoTuned = flywheelOutput.autoTuned;
|
|
54659
|
+
state.autoRelaxed = flywheelOutput.autoRelaxed;
|
|
54626
54660
|
state.research = loadResearchMetricsFromDisk(cwd);
|
|
54627
54661
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
54628
54662
|
saveFlywheelState(cwd, state);
|
|
@@ -56903,6 +56937,13 @@ async function runScan(options, explicitPaths) {
|
|
|
56903
56937
|
if (defaultOffRules.has(tuned.ruleId)) continue;
|
|
56904
56938
|
config.rules[tuned.ruleId] = tuned.severity;
|
|
56905
56939
|
}
|
|
56940
|
+
for (const relaxed of flywheelState.autoRelaxed) {
|
|
56941
|
+
if (config.rules[relaxed.ruleId] === "off") {
|
|
56942
|
+
continue;
|
|
56943
|
+
}
|
|
56944
|
+
if (defaultOffRules.has(relaxed.ruleId)) continue;
|
|
56945
|
+
config.rules[relaxed.ruleId] = relaxed.severity;
|
|
56946
|
+
}
|
|
56906
56947
|
}
|
|
56907
56948
|
const pool = new WorkerPool({
|
|
56908
56949
|
config,
|
package/dist/index.d.cts
CHANGED
|
@@ -1207,6 +1207,48 @@ interface AutoTunedRule {
|
|
|
1207
1207
|
severity: Severity;
|
|
1208
1208
|
reason: string;
|
|
1209
1209
|
}
|
|
1210
|
+
/**
|
|
1211
|
+
* v0.40.0 (Sprint 2.1): reverse direction of the flywheel ratchet.
|
|
1212
|
+
*
|
|
1213
|
+
* The flywheel has historically been one-way — `severityBump()` raises
|
|
1214
|
+
* a rule's severity after 3 consecutive top-3 appearances. The
|
|
1215
|
+
* relaxed half tells users when the data suggests a rule is *less*
|
|
1216
|
+
* relevant to *this* repository than the corpus default — i.e.
|
|
1217
|
+
* a high-FP rule that the user keeps ignoring.
|
|
1218
|
+
*
|
|
1219
|
+
* Distinct from `AutoTunedRule` on purpose:
|
|
1220
|
+
* - `severity` widens to `Severity | 'off'` because the relaxation
|
|
1221
|
+
* ratchets past `'low'` into `'off'` (the floor). Once off, the
|
|
1222
|
+
* read-side skips the rule entirely (same as a user `'off'`
|
|
1223
|
+
* override).
|
|
1224
|
+
* - `reason` references the ignore-streak and the corpus prior
|
|
1225
|
+
* that motivated the relaxation, so users can audit the
|
|
1226
|
+
* decision later (read `.slopbrick/flywheel/auto-tuned.json`).
|
|
1227
|
+
* - `previousSeverity` makes it explicit what we ratcheted *from*
|
|
1228
|
+
* (vs `AutoTunedRule` whose `severity` is just the new value;
|
|
1229
|
+
* the bump is always `prev + 1` so the prior is implied).
|
|
1230
|
+
* - Schema carries `defaultPrior` so re-calibration can replay
|
|
1231
|
+
* the relaxation against fresh corpus data (the relaxation
|
|
1232
|
+
* was based on a specific prior — if the prior changes, the
|
|
1233
|
+
* relaxation decision may no longer hold).
|
|
1234
|
+
*/
|
|
1235
|
+
interface AutoRelaxedRule {
|
|
1236
|
+
ruleId: string;
|
|
1237
|
+
/**
|
|
1238
|
+
* New effective severity. Floors at `'off'` (the ratchet
|
|
1239
|
+
* ultimate floor). Note the deliberate asymmetry with
|
|
1240
|
+
* `AutoTunedRule` which can never write `'off'` — the bump
|
|
1241
|
+
* direction saturates at `'high'`, but relaxation walks
|
|
1242
|
+
* out the other end.
|
|
1243
|
+
*/
|
|
1244
|
+
severity: Severity | 'off';
|
|
1245
|
+
previousSeverity: Severity;
|
|
1246
|
+
reason: string;
|
|
1247
|
+
/** AI-corpus prevalence assumed when the relaxation was decided. */
|
|
1248
|
+
defaultPrior: number;
|
|
1249
|
+
/** When the relaxation was decided (ISO 8601). */
|
|
1250
|
+
relaxedAt: string;
|
|
1251
|
+
}
|
|
1210
1252
|
interface RuleSuggestion {
|
|
1211
1253
|
pattern: string;
|
|
1212
1254
|
example: string;
|
|
@@ -1230,10 +1272,30 @@ interface FlywheelState {
|
|
|
1230
1272
|
version: string;
|
|
1231
1273
|
updatedAt: string;
|
|
1232
1274
|
autoTuned: AutoTunedRule[];
|
|
1275
|
+
/**
|
|
1276
|
+
* v0.40.0+: rules the flywheel has *relaxed* based on observed
|
|
1277
|
+
* ignore behavior across consecutive scans. Mirrors `autoTuned`
|
|
1278
|
+
* in lifecycle and persistence shape, but represents a downward
|
|
1279
|
+
* (not upward) severity adjustment. The next scan reads both
|
|
1280
|
+
* lists and applies them on top of the corpus-default severity.
|
|
1281
|
+
*
|
|
1282
|
+
* Persisted to `.slopbrick/flywheel/auto-tuned.json` alongside
|
|
1283
|
+
* `autoTuned` for backward compat with v0.39.x consumers of
|
|
1284
|
+
* the file (they just ignore the unknown field). Migration to
|
|
1285
|
+
* v3 happens in `migrateFlywheelState`.
|
|
1286
|
+
*/
|
|
1287
|
+
autoRelaxed: AutoRelaxedRule[];
|
|
1233
1288
|
research?: ResearchMetrics;
|
|
1234
1289
|
}
|
|
1235
1290
|
interface FlywheelOutput {
|
|
1236
1291
|
autoTuned: AutoTunedRule[];
|
|
1292
|
+
/**
|
|
1293
|
+
* v0.40.0+: relaxation candidates produced by the current scan.
|
|
1294
|
+
* Read by `persistRun.ts` which writes them into the next
|
|
1295
|
+
* persisted `FlywheelState`. Empty when no rule has hit the
|
|
1296
|
+
* `IGNORE_THRESHOLD` yet.
|
|
1297
|
+
*/
|
|
1298
|
+
autoRelaxed: AutoRelaxedRule[];
|
|
1237
1299
|
hotspotIssues: Issue[];
|
|
1238
1300
|
suggestions: RuleSuggestion[];
|
|
1239
1301
|
research?: ResearchMetrics;
|
|
@@ -1612,4 +1674,4 @@ declare function formatBadge(report: ProjectReport): string;
|
|
|
1612
1674
|
/** Render an array of values as a Unicode sparkline (▁▂▃▄▅▆▇█). */
|
|
1613
1675
|
declare function formatSparkline(values: number[]): string;
|
|
1614
1676
|
|
|
1615
|
-
export { AI_SECURITY_NUMERIC, type AiDebt, type AiMaintenanceCost, type AiMaintenanceCostResult, type AstroComponentFact, type AutoTunedRule, type BaselineCache, type BaselineMeta, type CachedFile, type Category, type ClassNameFact, type CommentFact, type ComponentFacts, type ComponentScore, type ComponentSizeFact, type ConsoleCallFact, type Constitution, DEFAULT_CONFIG, type DangerouslySetInnerHtmlFact, type DbDriftLevel, type DbFinding, type DialogCallFact, type DisabledLintRuleFact, type DocDriftLevel, type DocFinding, type DomQueryFact, type ElementFact, type EvalCallFact, type ExplicitAnyFact, type FetchCallFact, type FileScanResult, type FixSuggestion, type FlywheelOutput, type FlywheelState, type Framework$1 as Framework, type HealthFile, type HookCallFact, type HookDependencyArrayFact, type HookFact, type ImportFact, type InlineEventHandlerFact, type Issue, type JsxAttributeStringLiteralFact, type JsxTextLiteralFact, type KeyPropFact, type LogicalExpressionFact, type MagicNumberSpacingConfig, type MaintenanceAxes, type MaintenanceAxisHealth, type NonNullAssertionFact, type OptimisticUpdateFact, type ProjectReport, type PropMutationFact, type PropPassThroughFact, REPOSITORY_HEALTH_WEIGHTS, type ReportReadResult, type RepositoryHealth, type RepositoryHealthInputs, type ResearchMetrics, type ResolvedConfig, type Rule, type RuleContext, type RuleSeverity, type RuleSuggestion, type ScanCache, type ScanFacts, type ScanProjectOptions, type ScanSelfScanConfig, type Severity, type SlopAuditRun, type StateBinding, type StateBindingFact, type StringLiteralFact, type StylePropFact, type TamaguiStylePropFact, type TopOffender, type UseEffectBodyFact, VERSION, baselineStatusMessage, colorForSlop, failedThresholdCount, filterByDisabledDirectives, filterIssues, formatBadge, formatReportFromFile, formatSparkline, loadConfig, readReportFile, runCli, runInitWizard, scanProject, serializeConfig, stagedGating, thresholdExceeded };
|
|
1677
|
+
export { AI_SECURITY_NUMERIC, type AiDebt, type AiMaintenanceCost, type AiMaintenanceCostResult, type AstroComponentFact, type AutoRelaxedRule, type AutoTunedRule, type BaselineCache, type BaselineMeta, type CachedFile, type Category, type ClassNameFact, type CommentFact, type ComponentFacts, type ComponentScore, type ComponentSizeFact, type ConsoleCallFact, type Constitution, DEFAULT_CONFIG, type DangerouslySetInnerHtmlFact, type DbDriftLevel, type DbFinding, type DialogCallFact, type DisabledLintRuleFact, type DocDriftLevel, type DocFinding, type DomQueryFact, type ElementFact, type EvalCallFact, type ExplicitAnyFact, type FetchCallFact, type FileScanResult, type FixSuggestion, type FlywheelOutput, type FlywheelState, type Framework$1 as Framework, type HealthFile, type HookCallFact, type HookDependencyArrayFact, type HookFact, type ImportFact, type InlineEventHandlerFact, type Issue, type JsxAttributeStringLiteralFact, type JsxTextLiteralFact, type KeyPropFact, type LogicalExpressionFact, type MagicNumberSpacingConfig, type MaintenanceAxes, type MaintenanceAxisHealth, type NonNullAssertionFact, type OptimisticUpdateFact, type ProjectReport, type PropMutationFact, type PropPassThroughFact, REPOSITORY_HEALTH_WEIGHTS, type ReportReadResult, type RepositoryHealth, type RepositoryHealthInputs, type ResearchMetrics, type ResolvedConfig, type Rule, type RuleContext, type RuleSeverity, type RuleSuggestion, type ScanCache, type ScanFacts, type ScanProjectOptions, type ScanSelfScanConfig, type Severity, type SlopAuditRun, type StateBinding, type StateBindingFact, type StringLiteralFact, type StylePropFact, type TamaguiStylePropFact, type TopOffender, type UseEffectBodyFact, VERSION, baselineStatusMessage, colorForSlop, failedThresholdCount, filterByDisabledDirectives, filterIssues, formatBadge, formatReportFromFile, formatSparkline, loadConfig, readReportFile, runCli, runInitWizard, scanProject, serializeConfig, stagedGating, thresholdExceeded };
|
package/dist/index.d.ts
CHANGED
|
@@ -1207,6 +1207,48 @@ interface AutoTunedRule {
|
|
|
1207
1207
|
severity: Severity;
|
|
1208
1208
|
reason: string;
|
|
1209
1209
|
}
|
|
1210
|
+
/**
|
|
1211
|
+
* v0.40.0 (Sprint 2.1): reverse direction of the flywheel ratchet.
|
|
1212
|
+
*
|
|
1213
|
+
* The flywheel has historically been one-way — `severityBump()` raises
|
|
1214
|
+
* a rule's severity after 3 consecutive top-3 appearances. The
|
|
1215
|
+
* relaxed half tells users when the data suggests a rule is *less*
|
|
1216
|
+
* relevant to *this* repository than the corpus default — i.e.
|
|
1217
|
+
* a high-FP rule that the user keeps ignoring.
|
|
1218
|
+
*
|
|
1219
|
+
* Distinct from `AutoTunedRule` on purpose:
|
|
1220
|
+
* - `severity` widens to `Severity | 'off'` because the relaxation
|
|
1221
|
+
* ratchets past `'low'` into `'off'` (the floor). Once off, the
|
|
1222
|
+
* read-side skips the rule entirely (same as a user `'off'`
|
|
1223
|
+
* override).
|
|
1224
|
+
* - `reason` references the ignore-streak and the corpus prior
|
|
1225
|
+
* that motivated the relaxation, so users can audit the
|
|
1226
|
+
* decision later (read `.slopbrick/flywheel/auto-tuned.json`).
|
|
1227
|
+
* - `previousSeverity` makes it explicit what we ratcheted *from*
|
|
1228
|
+
* (vs `AutoTunedRule` whose `severity` is just the new value;
|
|
1229
|
+
* the bump is always `prev + 1` so the prior is implied).
|
|
1230
|
+
* - Schema carries `defaultPrior` so re-calibration can replay
|
|
1231
|
+
* the relaxation against fresh corpus data (the relaxation
|
|
1232
|
+
* was based on a specific prior — if the prior changes, the
|
|
1233
|
+
* relaxation decision may no longer hold).
|
|
1234
|
+
*/
|
|
1235
|
+
interface AutoRelaxedRule {
|
|
1236
|
+
ruleId: string;
|
|
1237
|
+
/**
|
|
1238
|
+
* New effective severity. Floors at `'off'` (the ratchet
|
|
1239
|
+
* ultimate floor). Note the deliberate asymmetry with
|
|
1240
|
+
* `AutoTunedRule` which can never write `'off'` — the bump
|
|
1241
|
+
* direction saturates at `'high'`, but relaxation walks
|
|
1242
|
+
* out the other end.
|
|
1243
|
+
*/
|
|
1244
|
+
severity: Severity | 'off';
|
|
1245
|
+
previousSeverity: Severity;
|
|
1246
|
+
reason: string;
|
|
1247
|
+
/** AI-corpus prevalence assumed when the relaxation was decided. */
|
|
1248
|
+
defaultPrior: number;
|
|
1249
|
+
/** When the relaxation was decided (ISO 8601). */
|
|
1250
|
+
relaxedAt: string;
|
|
1251
|
+
}
|
|
1210
1252
|
interface RuleSuggestion {
|
|
1211
1253
|
pattern: string;
|
|
1212
1254
|
example: string;
|
|
@@ -1230,10 +1272,30 @@ interface FlywheelState {
|
|
|
1230
1272
|
version: string;
|
|
1231
1273
|
updatedAt: string;
|
|
1232
1274
|
autoTuned: AutoTunedRule[];
|
|
1275
|
+
/**
|
|
1276
|
+
* v0.40.0+: rules the flywheel has *relaxed* based on observed
|
|
1277
|
+
* ignore behavior across consecutive scans. Mirrors `autoTuned`
|
|
1278
|
+
* in lifecycle and persistence shape, but represents a downward
|
|
1279
|
+
* (not upward) severity adjustment. The next scan reads both
|
|
1280
|
+
* lists and applies them on top of the corpus-default severity.
|
|
1281
|
+
*
|
|
1282
|
+
* Persisted to `.slopbrick/flywheel/auto-tuned.json` alongside
|
|
1283
|
+
* `autoTuned` for backward compat with v0.39.x consumers of
|
|
1284
|
+
* the file (they just ignore the unknown field). Migration to
|
|
1285
|
+
* v3 happens in `migrateFlywheelState`.
|
|
1286
|
+
*/
|
|
1287
|
+
autoRelaxed: AutoRelaxedRule[];
|
|
1233
1288
|
research?: ResearchMetrics;
|
|
1234
1289
|
}
|
|
1235
1290
|
interface FlywheelOutput {
|
|
1236
1291
|
autoTuned: AutoTunedRule[];
|
|
1292
|
+
/**
|
|
1293
|
+
* v0.40.0+: relaxation candidates produced by the current scan.
|
|
1294
|
+
* Read by `persistRun.ts` which writes them into the next
|
|
1295
|
+
* persisted `FlywheelState`. Empty when no rule has hit the
|
|
1296
|
+
* `IGNORE_THRESHOLD` yet.
|
|
1297
|
+
*/
|
|
1298
|
+
autoRelaxed: AutoRelaxedRule[];
|
|
1237
1299
|
hotspotIssues: Issue[];
|
|
1238
1300
|
suggestions: RuleSuggestion[];
|
|
1239
1301
|
research?: ResearchMetrics;
|
|
@@ -1612,4 +1674,4 @@ declare function formatBadge(report: ProjectReport): string;
|
|
|
1612
1674
|
/** Render an array of values as a Unicode sparkline (▁▂▃▄▅▆▇█). */
|
|
1613
1675
|
declare function formatSparkline(values: number[]): string;
|
|
1614
1676
|
|
|
1615
|
-
export { AI_SECURITY_NUMERIC, type AiDebt, type AiMaintenanceCost, type AiMaintenanceCostResult, type AstroComponentFact, type AutoTunedRule, type BaselineCache, type BaselineMeta, type CachedFile, type Category, type ClassNameFact, type CommentFact, type ComponentFacts, type ComponentScore, type ComponentSizeFact, type ConsoleCallFact, type Constitution, DEFAULT_CONFIG, type DangerouslySetInnerHtmlFact, type DbDriftLevel, type DbFinding, type DialogCallFact, type DisabledLintRuleFact, type DocDriftLevel, type DocFinding, type DomQueryFact, type ElementFact, type EvalCallFact, type ExplicitAnyFact, type FetchCallFact, type FileScanResult, type FixSuggestion, type FlywheelOutput, type FlywheelState, type Framework$1 as Framework, type HealthFile, type HookCallFact, type HookDependencyArrayFact, type HookFact, type ImportFact, type InlineEventHandlerFact, type Issue, type JsxAttributeStringLiteralFact, type JsxTextLiteralFact, type KeyPropFact, type LogicalExpressionFact, type MagicNumberSpacingConfig, type MaintenanceAxes, type MaintenanceAxisHealth, type NonNullAssertionFact, type OptimisticUpdateFact, type ProjectReport, type PropMutationFact, type PropPassThroughFact, REPOSITORY_HEALTH_WEIGHTS, type ReportReadResult, type RepositoryHealth, type RepositoryHealthInputs, type ResearchMetrics, type ResolvedConfig, type Rule, type RuleContext, type RuleSeverity, type RuleSuggestion, type ScanCache, type ScanFacts, type ScanProjectOptions, type ScanSelfScanConfig, type Severity, type SlopAuditRun, type StateBinding, type StateBindingFact, type StringLiteralFact, type StylePropFact, type TamaguiStylePropFact, type TopOffender, type UseEffectBodyFact, VERSION, baselineStatusMessage, colorForSlop, failedThresholdCount, filterByDisabledDirectives, filterIssues, formatBadge, formatReportFromFile, formatSparkline, loadConfig, readReportFile, runCli, runInitWizard, scanProject, serializeConfig, stagedGating, thresholdExceeded };
|
|
1677
|
+
export { AI_SECURITY_NUMERIC, type AiDebt, type AiMaintenanceCost, type AiMaintenanceCostResult, type AstroComponentFact, type AutoRelaxedRule, type AutoTunedRule, type BaselineCache, type BaselineMeta, type CachedFile, type Category, type ClassNameFact, type CommentFact, type ComponentFacts, type ComponentScore, type ComponentSizeFact, type ConsoleCallFact, type Constitution, DEFAULT_CONFIG, type DangerouslySetInnerHtmlFact, type DbDriftLevel, type DbFinding, type DialogCallFact, type DisabledLintRuleFact, type DocDriftLevel, type DocFinding, type DomQueryFact, type ElementFact, type EvalCallFact, type ExplicitAnyFact, type FetchCallFact, type FileScanResult, type FixSuggestion, type FlywheelOutput, type FlywheelState, type Framework$1 as Framework, type HealthFile, type HookCallFact, type HookDependencyArrayFact, type HookFact, type ImportFact, type InlineEventHandlerFact, type Issue, type JsxAttributeStringLiteralFact, type JsxTextLiteralFact, type KeyPropFact, type LogicalExpressionFact, type MagicNumberSpacingConfig, type MaintenanceAxes, type MaintenanceAxisHealth, type NonNullAssertionFact, type OptimisticUpdateFact, type ProjectReport, type PropMutationFact, type PropPassThroughFact, REPOSITORY_HEALTH_WEIGHTS, type ReportReadResult, type RepositoryHealth, type RepositoryHealthInputs, type ResearchMetrics, type ResolvedConfig, type Rule, type RuleContext, type RuleSeverity, type RuleSuggestion, type ScanCache, type ScanFacts, type ScanProjectOptions, type ScanSelfScanConfig, type Severity, type SlopAuditRun, type StateBinding, type StateBindingFact, type StringLiteralFact, type StylePropFact, type TamaguiStylePropFact, type TopOffender, type UseEffectBodyFact, VERSION, baselineStatusMessage, colorForSlop, failedThresholdCount, filterByDisabledDirectives, filterIssues, formatBadge, formatReportFromFile, formatSparkline, loadConfig, readReportFile, runCli, runInitWizard, scanProject, serializeConfig, stagedGating, thresholdExceeded };
|
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.
|
|
22
|
+
VERSION = "0.40.0";
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -50744,6 +50744,12 @@ function severityBump(severity) {
|
|
|
50744
50744
|
const idx = order.indexOf(severity);
|
|
50745
50745
|
return order[Math.min(idx + 1, order.length - 1)];
|
|
50746
50746
|
}
|
|
50747
|
+
function severityRelax(severity) {
|
|
50748
|
+
const order = ["off", "low", "medium", "high"];
|
|
50749
|
+
const idx = order.indexOf(severity);
|
|
50750
|
+
if (idx <= 0) return "off";
|
|
50751
|
+
return order[idx - 1];
|
|
50752
|
+
}
|
|
50747
50753
|
function resolveEffectiveSeverity(override, defaultSeverity) {
|
|
50748
50754
|
if (override === void 0 || override === "off" || override === "auto") {
|
|
50749
50755
|
return defaultSeverity;
|
|
@@ -50774,6 +50780,7 @@ function countConsecutiveTopFileAppearances(currentHash, recentTopHashes) {
|
|
|
50774
50780
|
}
|
|
50775
50781
|
function computeFlywheelOutput(runs, currentTopFiles, recentTopHashes, unmatchedStringLiterals, config, rules) {
|
|
50776
50782
|
const autoTuned = [];
|
|
50783
|
+
const autoRelaxed = [];
|
|
50777
50784
|
const hotspotIssues = [];
|
|
50778
50785
|
const suggestions = [];
|
|
50779
50786
|
const ruleById = new Map(rules.map((r) => [r.id, r]));
|
|
@@ -50794,6 +50801,29 @@ function computeFlywheelOutput(runs, currentTopFiles, recentTopHashes, unmatched
|
|
|
50794
50801
|
}
|
|
50795
50802
|
}
|
|
50796
50803
|
}
|
|
50804
|
+
if (runs.length >= IGNORE_THRESHOLD) {
|
|
50805
|
+
const window = runs.slice(-IGNORE_THRESHOLD);
|
|
50806
|
+
const candidateRuleIds = new Set(window.flatMap((r) => r.topOffenseIds));
|
|
50807
|
+
for (const ruleId of candidateRuleIds) {
|
|
50808
|
+
const rule = ruleById.get(ruleId);
|
|
50809
|
+
const defaultSeverity = rule?.severity ?? "medium";
|
|
50810
|
+
const currentSeverity = resolveEffectiveSeverity(
|
|
50811
|
+
config.rules[ruleId] ?? "auto",
|
|
50812
|
+
defaultSeverity
|
|
50813
|
+
);
|
|
50814
|
+
const streak = countConsecutiveTopOffenses(runs, ruleId);
|
|
50815
|
+
if (streak >= IGNORE_THRESHOLD) {
|
|
50816
|
+
autoRelaxed.push({
|
|
50817
|
+
ruleId,
|
|
50818
|
+
severity: severityRelax(currentSeverity),
|
|
50819
|
+
previousSeverity: currentSeverity,
|
|
50820
|
+
reason: `In top-3 offenses for ${streak} consecutive scans; corpus prior ${DEFAULT_RELAXATION_PRIOR.toFixed(2)} suggests the rule is high-FP for this corpus slice.`,
|
|
50821
|
+
defaultPrior: DEFAULT_RELAXATION_PRIOR,
|
|
50822
|
+
relaxedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
50823
|
+
});
|
|
50824
|
+
}
|
|
50825
|
+
}
|
|
50826
|
+
}
|
|
50797
50827
|
if (currentTopFiles.length > 0 && runs.length >= CONSECUTIVE_THRESHOLD) {
|
|
50798
50828
|
for (const file of currentTopFiles) {
|
|
50799
50829
|
const consecutive = countConsecutiveTopFileAppearances(file.hash, recentTopHashes);
|
|
@@ -50828,13 +50858,14 @@ function computeFlywheelOutput(runs, currentTopFiles, recentTopHashes, unmatched
|
|
|
50828
50858
|
});
|
|
50829
50859
|
}
|
|
50830
50860
|
}
|
|
50831
|
-
return { autoTuned, hotspotIssues, suggestions };
|
|
50861
|
+
return { autoTuned, autoRelaxed, hotspotIssues, suggestions };
|
|
50832
50862
|
}
|
|
50833
50863
|
function migrateFlywheelState(state) {
|
|
50834
50864
|
return {
|
|
50835
50865
|
version: FLYWHEEL_VERSION,
|
|
50836
50866
|
updatedAt: state.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
50837
50867
|
autoTuned: state.autoTuned ?? [],
|
|
50868
|
+
autoRelaxed: state.autoRelaxed ?? [],
|
|
50838
50869
|
research: state.research
|
|
50839
50870
|
};
|
|
50840
50871
|
}
|
|
@@ -50890,14 +50921,16 @@ function saveFlywheelState(cwd, state) {
|
|
|
50890
50921
|
function hashFile(filePath) {
|
|
50891
50922
|
return createHash8("sha256").update(filePath).digest("hex").slice(0, 16);
|
|
50892
50923
|
}
|
|
50893
|
-
var FLYWHEEL_DIR, STATE_FILE, CONSECUTIVE_THRESHOLD, FLYWHEEL_VERSION;
|
|
50924
|
+
var FLYWHEEL_DIR, STATE_FILE, CONSECUTIVE_THRESHOLD, IGNORE_THRESHOLD, DEFAULT_RELAXATION_PRIOR, FLYWHEEL_VERSION;
|
|
50894
50925
|
var init_flywheel = __esm({
|
|
50895
50926
|
"src/engine/flywheel.ts"() {
|
|
50896
50927
|
"use strict";
|
|
50897
50928
|
FLYWHEEL_DIR = ".slopbrick/flywheel";
|
|
50898
50929
|
STATE_FILE = "auto-tuned.json";
|
|
50899
50930
|
CONSECUTIVE_THRESHOLD = 3;
|
|
50900
|
-
|
|
50931
|
+
IGNORE_THRESHOLD = 5;
|
|
50932
|
+
DEFAULT_RELAXATION_PRIOR = 0.3;
|
|
50933
|
+
FLYWHEEL_VERSION = "3";
|
|
50901
50934
|
}
|
|
50902
50935
|
});
|
|
50903
50936
|
|
|
@@ -54610,6 +54643,7 @@ async function persistRun(input) {
|
|
|
54610
54643
|
);
|
|
54611
54644
|
const state = loadFlywheelState(cwd);
|
|
54612
54645
|
state.autoTuned = flywheelOutput.autoTuned;
|
|
54646
|
+
state.autoRelaxed = flywheelOutput.autoRelaxed;
|
|
54613
54647
|
state.research = loadResearchMetricsFromDisk(cwd);
|
|
54614
54648
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
54615
54649
|
saveFlywheelState(cwd, state);
|
|
@@ -56886,6 +56920,13 @@ async function runScan(options, explicitPaths) {
|
|
|
56886
56920
|
if (defaultOffRules.has(tuned.ruleId)) continue;
|
|
56887
56921
|
config.rules[tuned.ruleId] = tuned.severity;
|
|
56888
56922
|
}
|
|
56923
|
+
for (const relaxed of flywheelState.autoRelaxed) {
|
|
56924
|
+
if (config.rules[relaxed.ruleId] === "off") {
|
|
56925
|
+
continue;
|
|
56926
|
+
}
|
|
56927
|
+
if (defaultOffRules.has(relaxed.ruleId)) continue;
|
|
56928
|
+
config.rules[relaxed.ruleId] = relaxed.severity;
|
|
56929
|
+
}
|
|
56889
56930
|
}
|
|
56890
56931
|
const pool = new WorkerPool({
|
|
56891
56932
|
config,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slopbrick",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Discovered, modeled, and governed repository structure. SlopBrick scans source code, classifies it against 103 rules in 24 categories, computes 4 scores (aiSlopScore: lower=cleaner, engineeringHygiene, security, repositoryHealth composite), and persists the structure for AI agents and CI. v10-calibrated against 576,750 real files; v0.38.0 trims to 103 rules by deleting 37 v10-DORMANT rules.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|