stud-finder 0.3.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/README.md +21 -16
- data/SIGNALS.md +2 -2
- data/lib/stud_finder/cli.rb +29 -4
- data/lib/stud_finder/newness.rb +8 -2
- data/lib/stud_finder/scorer.rb +9 -7
- data/lib/stud_finder/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: faa8060a75346fc7a402adf203764c650d1596c2c6b3f4cb25571b3b21ecfa7a
|
|
4
|
+
data.tar.gz: 9c26eed3cb20c42d0155a730ba0ff70f9893a43493bf49d633aea4a0a2036a79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 863567f3e6dedd1808075bf9ae272cdb3f2e5b3b849b0eaa3b7b829a0db56873b757e6587d4fc82b6460fe6ab993407de24742fdde86d6bea6cb9809eb388ec0
|
|
7
|
+
data.tar.gz: a0a6d9b9fb0ad35bd88e22e26c24eb72957d22c46fd138e0e4c8b7da2bbcc885c5fb7f23bc58db547b826c754a478d72aa9b6cf0b3b04c81fa1801ef11fc903a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - Unreleased
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- BREAKING: Evidence formula changed — coverage is now a bonus, not a required third signal. The new formula is `max(history_only, with_coverage)` where `history_only = (age + commits) / 2`. Mature files without a coverage report now produce `evidence: 1.0` instead of the old ceiling of `0.6667`. Files with coverage data can only equal or exceed the history-only baseline, never fall below it.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Shallow-clone evidence null in CI: when Stud Finder detects a depth-1 clone (GitHub Actions default), it now automatically runs `git fetch --unshallow` (45 second timeout) before computing newness metadata. Repos that used the default `actions/checkout` without `fetch-depth: 0` now get real evidence values instead of `null` on every row.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `--no-auto-unshallow` flag: opt out of the automatic `git fetch --unshallow` on shallow clones. When set, Stud Finder behaves as before — shallow clone disables newness and emits only `shallow_clone_newness_disabled`.
|
|
21
|
+
- `shallow_clone_unshallow_failed` warning code: fires when auto-unshallow was attempted but failed (network error, timeout, or non-zero exit). Appears alongside `shallow_clone_newness_disabled` so consumers can distinguish "unshallow was never tried" from "unshallow was tried and failed".
|
|
22
|
+
|
|
23
|
+
## [0.4.0] - Unreleased
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- BREAKING: Classification thresholds are now percentile-based, not score-value-based. `--trunk-threshold 85` and `--branch-threshold 50` now mean "top 15% and top 50% of files by composite score" — not "score value ≥ 0.85 / 0.50". This is a semantic change to the same flag values; the numbers you pass stay the same but their meaning inverts. Previously, trunk was unreachable at defaults (max observed composite score ~0.717 in real repos, zero trunks); now every repo of meaningful size produces some trunks and branches. Absolute floors (raw complexity ≥ 15, raw fan_in ≥ 25) still escalate to `branch` regardless of percentile.
|
|
28
|
+
|
|
8
29
|
## [0.3.0] - 2026-07-11
|
|
9
30
|
|
|
10
31
|
### Fixed
|
|
@@ -38,7 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
38
59
|
|
|
39
60
|
### Added
|
|
40
61
|
|
|
41
|
-
- Initial
|
|
62
|
+
- Initial RubyGems release: a CLI that ranks files by five risk signals: fan-in (blast radius), fan-out, cyclomatic complexity, git churn, and test coverage.
|
|
42
63
|
- Temporal coupling analysis for identifying files that change together.
|
|
43
64
|
- Diff mode for scoring only files changed in a pull request while preserving repo-relative scores.
|
|
44
65
|
- Ruby and JavaScript/TypeScript support.
|
data/README.md
CHANGED
|
@@ -9,12 +9,12 @@ $ bundle exec bin/stud-finder ./my-rails-app
|
|
|
9
9
|
|
|
10
10
|
Ruby
|
|
11
11
|
rank language file score evidence class new age_days escalation ...
|
|
12
|
-
1 ruby app/models/proficiency.rb 0.7304 1.0000
|
|
13
|
-
2 ruby app/services/payment_service.rb 0.6890 1.0000
|
|
14
|
-
3 ruby app/controllers/orders_ctlr.rb 0.5721
|
|
12
|
+
1 ruby app/models/proficiency.rb 0.7304 1.0000 trunk false 842 ...
|
|
13
|
+
2 ruby app/services/payment_service.rb 0.6890 1.0000 trunk false 611 ...
|
|
14
|
+
3 ruby app/controllers/orders_ctlr.rb 0.5721 1.0000 branch false 520 ...
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
*Scores and evidence are illustrative. Row 3 has no coverage data (evidence
|
|
17
|
+
*Scores and evidence are illustrative. Row 3 has full history but no coverage data (evidence `1.0000` — coverage is a bonus, not a requirement); rows 1–2 have full coverage (evidence `1.0000`). The `class` column reflects percentile rank of composite score within the repo — see Classification.*
|
|
18
18
|
|
|
19
19
|
The full table adds `fan_in`, `fan_out`, `instability`, `complexity`, `churn_commits`, `churn_lines`, `churn_pct`, `loc`, `loc_pct`, `max_coupling`, `max_coupling_partner`, `coupling_partners`, `coupling_pct`, and `coverage`. Use `--output json` for machine-readable output including a `warnings` section and full `meta`.
|
|
20
20
|
|
|
@@ -117,15 +117,19 @@ Result is clamped to `[0.0, 1.0]` and rounded to four decimal places.
|
|
|
117
117
|
|
|
118
118
|
## Classification
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
Files are classified into three tiers based on the **percentile rank of their composite score** within the repo:
|
|
121
121
|
|
|
122
|
-
- **trunk** —
|
|
123
|
-
- **branch** —
|
|
124
|
-
- **leaf** —
|
|
122
|
+
- **trunk** — top 15% by composite score (default `--trunk-threshold 85`). Load-bearing. High review bar, change with care.
|
|
123
|
+
- **branch** — top 50% but below top 15% (default `--branch-threshold 50`). Meaningful coupling.
|
|
124
|
+
- **leaf** — everything below the 50th percentile. Isolated. Move fast here.
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
This means every repo of meaningful size has trunks: a file at score 0.55 is trunk if the rest of the repo scores below it. The absolute floors below provide a safety net for tiny repos.
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
**Absolute floors:** raw complexity ≥ 15 or raw fan-in ≥ 25 escalates a `leaf` to `branch` regardless of percentile. This ensures that structurally dangerous files in tiny repos (where percentile spread is minimal) still receive elevated attention.
|
|
129
|
+
|
|
130
|
+
**Tiny repos:** In repos with very few files and uniform scores, the percentile spread may place all files at the same score_pct (0.0), resulting in 0 trunks. The absolute floor is the escape hatch for dangerous files in this case.
|
|
131
|
+
|
|
132
|
+
Note: `--trunk-threshold 85` and `--branch-threshold 50` now mean "top 15% / top 50% of files by composite score" — not "score value ≥ 0.85 / 0.50". This is a BREAKING semantic change from 0.3.0. Trunk was previously unreachable at defaults (max observed composite score ~0.717 in real repos); this change restores the guarantee that some files are always classified trunk-tier relative to their codebase.
|
|
129
133
|
|
|
130
134
|
### Absolute floors
|
|
131
135
|
|
|
@@ -143,7 +147,7 @@ A file is considered new when its first commit is within `--new-file-days` days
|
|
|
143
147
|
|
|
144
148
|
A stronger rule runs first: if a new file depends on a structurally `trunk` file through its fan-out edges, it escalates to `trunk` with `escalation=trunk_adjacent`. This highlights new code consuming critical interfaces, where contract-violation risk is highest. Use `--no-newness` to disable both newness rules.
|
|
145
149
|
|
|
146
|
-
**CI usage:** newness rules require full git history. In GitHub Actions, set `fetch-depth: 0` before running Stud Finder. If Stud Finder detects a shallow clone
|
|
150
|
+
**CI usage:** newness rules require full git history. In GitHub Actions, set `fetch-depth: 0` before running Stud Finder. If Stud Finder detects a shallow clone it automatically runs `git fetch --unshallow` (45 second timeout) to recover full history before computing metadata. If the fetch fails (network error, timeout, offline runner), Stud Finder falls back to disabled newness and emits both `shallow_clone_newness_disabled` and `shallow_clone_unshallow_failed` in `warnings`. Pass `--no-auto-unshallow` to skip the fetch attempt entirely — on shallow clones only `shallow_clone_newness_disabled` fires and classifications match `--no-newness`.
|
|
147
151
|
|
|
148
152
|
---
|
|
149
153
|
|
|
@@ -151,9 +155,9 @@ A stronger rule runs first: if a new file depends on a structurally `trunk` file
|
|
|
151
155
|
|
|
152
156
|
Every row carries an `evidence` value (0.0–1.0) alongside `score`. Score is the weighted signal composite. Evidence is a metadata confidence: how much history + coverage-data backing does that score have?
|
|
153
157
|
|
|
154
|
-
Evidence combines file age
|
|
158
|
+
Evidence combines file age and commit count as its base, with coverage data as a bonus signal. The formula is `max(history_only, with_coverage)`, where `history_only = (age + commits) / 2` and `with_coverage = (age + commits + 1.0) / 3`. A mature file with full history always reaches `1.0` regardless of whether a coverage report is present — coverage can only raise evidence above the history baseline, never suppress it. A high score with low evidence means "structural signals concentrated risk here, but we're not certain because the file is young or the history is thin." A high score with high evidence is a strong claim.
|
|
155
159
|
|
|
156
|
-
In shallow clones (
|
|
160
|
+
In shallow clones where auto-unshallow fails (or `--no-auto-unshallow` is set), evidence is `null` on every row because file-metadata history is unavailable. See the CI note above.
|
|
157
161
|
|
|
158
162
|
**Gate consumers should threshold `class` for verdicts and `evidence` for confidence, not raw `score` alone.** Output is sorted by `(class_rank, score)` so trunks group above branches above leaves, and `--top N` no longer drops newness-escalated `trunk_adjacent` files behind high-score branches.
|
|
159
163
|
|
|
@@ -163,7 +167,8 @@ In shallow clones (git fetch-depth < full history), evidence is `null` on every
|
|
|
163
167
|
|
|
164
168
|
`analysis.warnings` (available in JSON output) surfaces conditions the run detected that a consumer should know about:
|
|
165
169
|
|
|
166
|
-
- **`shallow_clone_newness_disabled`** — shallow git clone detected; newness rules auto-disabled.
|
|
170
|
+
- **`shallow_clone_newness_disabled`** — shallow git clone detected; newness rules auto-disabled (auto-unshallow also failed, or `--no-auto-unshallow` was passed).
|
|
171
|
+
- **`shallow_clone_unshallow_failed`** — `git fetch --unshallow` was attempted but failed (network error or timeout); evidence is unavailable. Use `fetch-depth: 0` in CI or pass `--no-auto-unshallow` to suppress the attempt.
|
|
167
172
|
- **`insufficient_dispersion_<signal>`** — every file in the codebase has the same non-zero raw value for `<signal>`, so its percentile-ranked contribution collapsed to `0.0`. The score is unchanged; the warning flags that the signal is silently uninformative rather than genuinely absent. One per affected signal: `fan_in`, `fan_out`, `complexity`, `churn`, `coverage`, `interaction`, `coupling`.
|
|
168
173
|
- Language-specific warnings such as `js_depcruise_no_config` when the JS pipeline had to fall back.
|
|
169
174
|
|
|
@@ -218,8 +223,8 @@ Each language gets its own ranking section in the output — Ruby and JS are not
|
|
|
218
223
|
| `--weights WEIGHTS` | Custom weights as fractions, e.g. `fan_in:F,fan_out:O,complexity:C,churn:H,coverage:V[,interaction:I][,coupling:P]`. The five base keys (`fan_in`, `fan_out`, `complexity`, `churn`, `coverage`) are required. `interaction` and `coupling` are optional: when omitted, `interaction` defaults to `0.0` (custom weights opt-in) and `coupling` defaults to `0.05`. Each value must be in `[0.0, 1.0]`. When no coverage data is provided, `coverage` must be `0.0`. |
|
|
219
224
|
| `--interaction-weight N` | Sugar flag for setting only the interaction weight. |
|
|
220
225
|
| `--coupling-weight N` | Sugar flag for setting only the coupling weight. Bounds-checked `[0.0, 1.0]`. |
|
|
221
|
-
| `--trunk-threshold N` |
|
|
222
|
-
| `--branch-threshold N` |
|
|
226
|
+
| `--trunk-threshold N` | composite-score percentile cutoff for trunk classification; top (100-N)% of files by score (default: 85) |
|
|
227
|
+
| `--branch-threshold N` | composite-score percentile cutoff for branch classification; top (100-N)% of files by score (default: 50) |
|
|
223
228
|
| `--exclude PATTERN` | Exclude glob pattern (repeatable). `spec/` and `test/` excluded by default. |
|
|
224
229
|
| `--top N` | Emit only the top N results |
|
|
225
230
|
| `--diff-base REF` | Score the whole repo but emit only the files changed on `HEAD` vs the merge-base with `REF` (e.g. `origin/staging`). Ranks and scores stay relative to the full repo. Ideal for per-PR runs. |
|
data/SIGNALS.md
CHANGED
|
@@ -88,8 +88,8 @@ Ties receive the same rank. Edge cases: a codebase with only one file gets `0.0`
|
|
|
88
88
|
Three pieces of output serve three purposes. Confusing them causes gate consumers to threshold the wrong number.
|
|
89
89
|
|
|
90
90
|
- **`score`** (0.0–1.0, four decimals) — the weighted composite. This is **evidence about the file's structural risk**, nothing more. Higher score means the signals concentrated more risk on this file.
|
|
91
|
-
- **`class`** (`leaf` / `branch` / `trunk`) — the decision label. Driven by the composite
|
|
92
|
-
- **`evidence`** (0.0–1.0) — a metadata confidence value based on file age, commit count, and
|
|
91
|
+
- **`class`** (`leaf` / `branch` / `trunk`) — the decision label. Driven by the composite-score PERCENTILE across the codebase against configurable thresholds (defaults: `branch` at top 50%, `trunk` at top 15%). This guarantees that some files are always trunk-tier relative to their repo — a file scoring 0.55 can be trunk if the rest of the codebase scores below it. This is what a gate should threshold on for verdicts.
|
|
92
|
+
- **`evidence`** (0.0–1.0) — a metadata confidence value based on file age, commit count, and optionally coverage presence. The formula is `max(history_only, with_coverage)`, where `history_only = (age + commits) / 2` and `with_coverage = (age + commits + 1.0) / 3` (only included when coverage data is present). Coverage is a bonus: a mature file with full history always reaches `1.0` even without a coverage report. A high score with low evidence means "structural signals concentrated risk here, but we're not certain because the file is young or the history is thin." Gates should threshold `evidence` for confidence, not raw `score`.
|
|
93
93
|
|
|
94
94
|
### Absolute floors
|
|
95
95
|
|
data/lib/stud_finder/cli.rb
CHANGED
|
@@ -63,6 +63,7 @@ module StudFinder
|
|
|
63
63
|
newness: true,
|
|
64
64
|
new_file_days: StudFinder::Newness::DEFAULT_DAYS,
|
|
65
65
|
new_file_min_commits: StudFinder::Newness::DEFAULT_MIN_COMMITS,
|
|
66
|
+
auto_unshallow: true,
|
|
66
67
|
cli_warnings: []
|
|
67
68
|
}.freeze
|
|
68
69
|
|
|
@@ -209,11 +210,13 @@ module StudFinder
|
|
|
209
210
|
@options[:rails_inference] = false
|
|
210
211
|
end
|
|
211
212
|
opts.on('--trunk-threshold N', Integer,
|
|
212
|
-
'score cutoff for trunk classification, integer 1-99
|
|
213
|
+
'score percentile cutoff for trunk classification, integer 1-99 ' \
|
|
214
|
+
'(default: 85 = top 15% of files by score)') do |value|
|
|
213
215
|
@options[:trunk_threshold] = value
|
|
214
216
|
end
|
|
215
217
|
opts.on('--branch-threshold N', Integer,
|
|
216
|
-
'score cutoff for branch classification, integer 1-99
|
|
218
|
+
'score percentile cutoff for branch classification, integer 1-99 ' \
|
|
219
|
+
'(default: 50 = top 50% of files by score)') do |value|
|
|
217
220
|
@options[:branch_threshold] = value
|
|
218
221
|
end
|
|
219
222
|
opts.on('--exclude PATTERN', 'Exclude glob pattern (repeatable)') do |value|
|
|
@@ -257,6 +260,9 @@ module StudFinder
|
|
|
257
260
|
opts.on('--no-newness', 'Disable new-file classification rules') do
|
|
258
261
|
@options[:newness] = false
|
|
259
262
|
end
|
|
263
|
+
opts.on('--no-auto-unshallow', 'Disable automatic git fetch --unshallow on shallow clones') do
|
|
264
|
+
@options[:auto_unshallow] = false
|
|
265
|
+
end
|
|
260
266
|
opts.on('--verbose', 'Print suppressed per-file warnings to stderr') do
|
|
261
267
|
@options[:verbose] = true
|
|
262
268
|
end
|
|
@@ -509,8 +515,12 @@ module StudFinder
|
|
|
509
515
|
|
|
510
516
|
def apply_newness(files, edges, rows, coverage_result = nil)
|
|
511
517
|
metadata = if @options[:newness] && Newness.shallow_repository?(@repo_path)
|
|
512
|
-
@options[:
|
|
513
|
-
|
|
518
|
+
if @options[:auto_unshallow]
|
|
519
|
+
attempt_unshallow(files)
|
|
520
|
+
else
|
|
521
|
+
@options[:cli_warnings] << Newness::SHALLOW_CLONE_WARNING
|
|
522
|
+
Newness.disabled_metadata(files)
|
|
523
|
+
end
|
|
514
524
|
elsif @options[:newness]
|
|
515
525
|
Newness.new(repo_path: @repo_path, files: files, days: @options[:new_file_days],
|
|
516
526
|
min_commits: @options[:new_file_min_commits]).call
|
|
@@ -520,6 +530,21 @@ module StudFinder
|
|
|
520
530
|
Newness.apply(rows: rows, edges: edges, metadata: metadata, coverage: coverage_result)
|
|
521
531
|
end
|
|
522
532
|
|
|
533
|
+
def attempt_unshallow(files)
|
|
534
|
+
require 'timeout'
|
|
535
|
+
Timeout.timeout(45) do
|
|
536
|
+
_stdout, _stderr, status = Open3.capture3('git', 'fetch', '--unshallow', chdir: @repo_path)
|
|
537
|
+
raise 'unshallow failed' unless status.success?
|
|
538
|
+
|
|
539
|
+
Newness.new(repo_path: @repo_path, files: files, days: @options[:new_file_days],
|
|
540
|
+
min_commits: @options[:new_file_min_commits]).call
|
|
541
|
+
end
|
|
542
|
+
rescue StandardError
|
|
543
|
+
@options[:cli_warnings] << Newness::SHALLOW_CLONE_WARNING
|
|
544
|
+
@options[:cli_warnings] << Newness::UNSHALLOW_FAILED_WARNING
|
|
545
|
+
Newness.disabled_metadata(files)
|
|
546
|
+
end
|
|
547
|
+
|
|
523
548
|
def loc_percentiles_by_language(files, loc, language_by_file)
|
|
524
549
|
files.group_by { |file| language_by_file.fetch(file) }.values.reduce({}) do |pct, language_files|
|
|
525
550
|
pct.merge(Normalizer.percentile_rank(loc, language_files))
|
data/lib/stud_finder/newness.rb
CHANGED
|
@@ -14,6 +14,10 @@ module StudFinder
|
|
|
14
14
|
code: 'shallow_clone_newness_disabled',
|
|
15
15
|
message: 'shallow git clone detected; newness rules disabled (use fetch-depth: 0 in CI for full newness behavior)'
|
|
16
16
|
}.freeze
|
|
17
|
+
UNSHALLOW_FAILED_WARNING = {
|
|
18
|
+
code: 'shallow_clone_unshallow_failed',
|
|
19
|
+
message: 'auto-unshallow failed; evidence unavailable (use fetch-depth: 0 in CI or pass --no-auto-unshallow)'
|
|
20
|
+
}.freeze
|
|
17
21
|
|
|
18
22
|
History = Struct.new(:first_commit_epoch, :total_commits, keyword_init: true)
|
|
19
23
|
|
|
@@ -104,9 +108,11 @@ module StudFinder
|
|
|
104
108
|
|
|
105
109
|
age_component = [metadata.fetch(:age_days, 0) / 30.0, 1.0].min
|
|
106
110
|
commits_component = [metadata.fetch(:total_commits, 0) / 3.0, 1.0].min
|
|
107
|
-
coverage_component = explicit_coverage ? 1.0 : 0.0
|
|
108
111
|
|
|
109
|
-
(
|
|
112
|
+
history_only = (age_component + commits_component) / 2.0
|
|
113
|
+
with_coverage = ((age_component + commits_component + 1.0) / 3.0) if explicit_coverage
|
|
114
|
+
|
|
115
|
+
[history_only, with_coverage].compact.max.round(4)
|
|
110
116
|
end
|
|
111
117
|
|
|
112
118
|
def self.git_shallow_file?(repo_path)
|
data/lib/stud_finder/scorer.rb
CHANGED
|
@@ -52,9 +52,11 @@ module StudFinder
|
|
|
52
52
|
}
|
|
53
53
|
@warnings = insufficient_dispersion_warnings(pcts)
|
|
54
54
|
|
|
55
|
+
scores = @files.to_h { |file| [file, weighted_score(file, pcts)] }
|
|
56
|
+
score_pcts = Normalizer.percentile_rank(scores, @files)
|
|
57
|
+
|
|
55
58
|
rows = @files.each_with_index.map do |file, index|
|
|
56
|
-
|
|
57
|
-
[index, result_row(file, score, pcts)]
|
|
59
|
+
[index, result_row(file, scores.fetch(file), pcts, score_pcts.fetch(file))]
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
rows.sort_by { |index, row| [-row[:score], index] }
|
|
@@ -123,12 +125,12 @@ module StudFinder
|
|
|
123
125
|
end
|
|
124
126
|
end
|
|
125
127
|
|
|
126
|
-
def result_row(file, score, pcts)
|
|
128
|
+
def result_row(file, score, pcts, score_pct)
|
|
127
129
|
fi = @fan_in.fetch(file, 0).to_i
|
|
128
130
|
fo = @fan_out.fetch(file, 0).to_i
|
|
129
131
|
rounded_score = score.round(4)
|
|
130
132
|
complexity = @complexity.fetch(file, 0).to_i
|
|
131
|
-
floored_class, floor_escalation = floored_classification(classification(
|
|
133
|
+
floored_class, floor_escalation = floored_classification(classification(score_pct), complexity, fi)
|
|
132
134
|
{
|
|
133
135
|
path: file,
|
|
134
136
|
score: rounded_score,
|
|
@@ -242,9 +244,9 @@ module StudFinder
|
|
|
242
244
|
end
|
|
243
245
|
end
|
|
244
246
|
|
|
245
|
-
def classification(
|
|
246
|
-
return 'trunk' if
|
|
247
|
-
return 'branch' if
|
|
247
|
+
def classification(score_pct)
|
|
248
|
+
return 'trunk' if score_pct >= @trunk_threshold / 100.0
|
|
249
|
+
return 'branch' if score_pct >= @branch_threshold / 100.0
|
|
248
250
|
|
|
249
251
|
'leaf'
|
|
250
252
|
end
|
data/lib/stud_finder/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stud-finder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- bazfer
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: csv
|
|
@@ -180,7 +180,7 @@ metadata:
|
|
|
180
180
|
changelog_uri: https://github.com/bazfer/stud-finder/blob/main/CHANGELOG.md
|
|
181
181
|
bug_tracker_uri: https://github.com/bazfer/stud-finder/issues
|
|
182
182
|
rubygems_mfa_required: 'true'
|
|
183
|
-
post_install_message:
|
|
183
|
+
post_install_message:
|
|
184
184
|
rdoc_options: []
|
|
185
185
|
require_paths:
|
|
186
186
|
- lib
|
|
@@ -195,8 +195,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
195
195
|
- !ruby/object:Gem::Version
|
|
196
196
|
version: '0'
|
|
197
197
|
requirements: []
|
|
198
|
-
rubygems_version: 3.
|
|
199
|
-
signing_key:
|
|
198
|
+
rubygems_version: 3.4.10
|
|
199
|
+
signing_key:
|
|
200
200
|
specification_version: 4
|
|
201
201
|
summary: Rank files by structural risk in Ruby and JavaScript/TypeScript codebases.
|
|
202
202
|
test_files: []
|