Add additive numeric scoring and configurable confidence intervals with supporting tests - #65
Add additive numeric scoring and configurable confidence intervals with supporting tests#65omkar-foss wants to merge 5 commits into
Conversation
35614b8 to
c3ccd9b
Compare
|
Just for reference, implementation of numeric scoring here is based on this: #12 (comment) |
andrew
left a comment
There was a problem hiding this comment.
Thanks for taking this on, and for the thorough tests.
The main thing I'd like to revisit is the consolidation step. ConsolidateFindingScore takes the max score per detector and then averages across detectors, so a commit with just a Co-Authored-By trailer scores 85, but the same commit with an additional tool mention in the message body drops to 52.5. Adding corroborating evidence shouldn't lower the score. #12 was heading toward something additive (SpamAssassin-style) rather than an average — probably worth pinning that down on the issue before reworking the code.
Related: the repo-wide OverallScore pools every finding from every commit into one max-per-detector average, so for a 500-commit range with one AI commit it just reports that one commit's score. I'm not sure a single number across a range is meaningful; the per-commit score is the useful bit.
A couple of structural things:
confidenceScoresandscan.Weightsare package-level mutable state set from the CLI.--confidence-scoreschanges what every detector reports asConfidenceand never resets, which leaks acrossRun()calls (and between tests —TestRunScanScoreFlagsleaves it modified). Would prefer these threaded through as arguments rather than globals.SetConfidenceScoresFromStringsdoesn't check the thresholds are ordered, solow=50,medium=30makesScoreToConfidence(40)return low.
Minor:
strconv.ParseFloat(fmt.Sprintf("%.2f", overall), 64)→math.Round(overall*100)/100- Replit Agent and Assistant used to be medium vs low confidence; both are now
TrailerMatchBaseScore— intentional? - The IIFE in
FormatJSONFindingscan be a plain local. - Stray blank line at
committer.go:28, typooverridencein detection.go.
The hash-slicing panic fix and the ConfidenceFromString move are both good and would happily take those as a separate PR if you want them in sooner.
|
Thanks for your review, my comments below.
Yes the max part is intentional, and this scoring indeed should be additive. But I guess we also need to have better weight defaults to avoid the score drops. In your case, score drops to 52.5 because both detectors get equal weights by default so 85x0.5 (trailer) + 20x0.5 (toolmention) = 52.5. I've used weights to normalize the score so that it always stays between 0 and 100 to automatically adjust for new detectors in future. Could you try with custom weights via cli?
Yes currently overall score is based on weighted average of findings across all commits. Makes sense, I'll update it to show score per commit (it's already in there just not using it yet). Will also resolve the other 6 points (structural and minor) along with these changes. Thanks |
|
Tried it. With I'd rather the consolidated score be Can revisit an additive scheme from #12 later if max turns out to be too coarse. |
Thanks for trying it out. I'll update this to use max, then let's try it out again. Yes agreed, if that too doesn't work well then we can revise to just have simple additive scoring. |
|
Got a couple conflicts that need resolving here |
No problem, will resolve in next push with these changes. Thanks |
Signed-off-by: Omkar P <45419097+omkar-foss@users.noreply.github.com>
Signed-off-by: Omkar P <45419097+omkar-foss@users.noreply.github.com>
Signed-off-by: Omkar P <45419097+omkar-foss@users.noreply.github.com>
@andrew I digged a little deeper, and also tried max for overall score, one major problem with using max seems to be that in case the highest score signal is a false positive, then it gets a boost over true positives. And another problem is that the lower score contributions of other detectors get foreshadowed by the highest score detector. e.g. say trailer=85 and committer=75, if trailer score is a false positive then it foreshadows committer and gives an overall score of 85. These problems don't occur with additive scoring. So like you suggested, I think it's best if we stick to the original additive scoring like we discussed in #12, I had documented it in this example, and it's based on SpamAssassin-like scoring. Let me know if this direction works for you, I have the additive changes ready since I was comparing it with weighted average and max on my system. Once you give a go, I'll push it. Thanks |
375f38f to
7cfeeb5
Compare
|
I've rebased for now, tests will pass after changes are pushed. |
|
Yep sounds good, tests still failing btw |
8d244d6 to
7cfeeb5
Compare
Signed-off-by: Omkar P <45419097+omkar-foss@users.noreply.github.com>
Done, I've pushed the additive scoring changes, tests passing now. Let me know if any changes needed, thanks |
andrew
left a comment
There was a problem hiding this comment.
Thanks for switching to the additive sum and dropping weights, that's the shape I was after. The per-commit (score: %.1f) in text output and the hash-slice guard both look good.
The sum is now uncapped but still rendered as Overall score: %.1f / 100 in both FormatText and FormatTextFindings. A typical Claude Code commit (committer 95 + trailer 85 + toolmention 20) prints 200.0 / 100. Either clamp the total in CalculateTotalScore or drop the / 100 from the label; unbounded SpamAssassin-style is fine, just don't claim a denominator. The same uncapping bites the EntireIO check: 35 + (n-1)×20 is passed to ScoreToConfidence, which errors above 100 and falls through to ConfidenceNone, so a commit with enough matching trailers reports zero confidence.
Summary.OverallScore still pools every finding from every commit into one max-per-detector sum, so a 500-commit range with one AI commit reports that commit's score as the range score. Now that per-commit scores are printed I'd drop the range-wide number rather than try to give it a meaning.
A couple of things from the last round are still open. confidenceScores is still package-level state mutated by SetConfidenceScoresFromStrings, so it leaks across Run() calls and between tests; I'd rather it was threaded through than global. SetConfidenceScoresFromStrings also still doesn't check the thresholds are ordered, so --confidence-scores=low=50,medium=30 makes Medium unreachable. And Replit Agent vs Assistant are still both TrailerMatchBaseScore where they used to be Medium vs Low; if that's deliberate just say so.
Smaller bits, none blocking on their own: ConfidenceNone = 0 has no type annotation where its siblings do; filterReport sets Score on the rebuilt CommitResult but not PerDetectorScores, so filtered JSON has "score": 85, "per_detector_scores": null; --confidence-scores is wired to scan but not text; the IIFE in FormatJSONFindings, the blank line at committer.go:28, and the overridence typo in detection.go are all still there.
|
Sure thanks, let's keep it uncapped and yeah I'll remove the overall score at report level, I think it's causing too much confusion. Regarding other minor things, my comments below
Yes this is pending on me, I'll add this, thanks
Yeah I understand that we should add a validation for this, I'll add it.
Yes, this is deliberate :)
I'll add this.
That's because overall score in the report is calculated directly using the findings, so it doesn't need the per detector scores separately. Anyway I'll remove as it's used for overall score.
Currently text doesn't need it since all findings have the same score (toolmention base score) and so allowing confidence levels won't be necessary. I suppose we could add it in future when text (toolmention) confidence scores are more varied.
Oops! I'll check these out, thanks :P |
Signed-off-by: Omkar P <45419097+omkar-foss@users.noreply.github.com>
|
All things in here resolved in this commit, also added some more missing tests. Let me know if any more changes needed, thanks |
Closes #12 and #74.
This PR adds additive numeric scoring (example here) and configurable configurable intervals (default ones are low=0 to 30, medium=31 to 70, high=71 to 100). Also adds supporting tests (which is most of the diff here).
Additionally: