evalaudit 0.3.0__tar.gz → 0.5.0__tar.gz

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.
Files changed (45) hide show
  1. evalaudit-0.5.0/CHANGELOG.md +840 -0
  2. {evalaudit-0.3.0 → evalaudit-0.5.0}/PKG-INFO +57 -33
  3. {evalaudit-0.3.0 → evalaudit-0.5.0}/README.md +56 -32
  4. evalaudit-0.5.0/analysis/README.md +108 -0
  5. evalaudit-0.5.0/analysis/mt-bench.md +270 -0
  6. evalaudit-0.5.0/analysis/output/q1_comparisons_ratings.csv +7 -0
  7. evalaudit-0.5.0/analysis/q1_check_v021.py +97 -0
  8. evalaudit-0.5.0/analysis/q1_leaderboard.py +332 -0
  9. evalaudit-0.5.0/analysis/q2_human_agreement.py +288 -0
  10. evalaudit-0.5.0/analysis/q3_alignment.py +230 -0
  11. evalaudit-0.5.0/analysis/q5_judge_against_human_baseline.py +617 -0
  12. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/_types.py +640 -211
  13. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/agreement.py +12 -7
  14. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/audit.py +1182 -910
  15. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/compare.py +713 -712
  16. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/judge.py +496 -13
  17. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/pairwise.py +12 -10
  18. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/power.py +14 -14
  19. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/scores.py +6 -4
  20. {evalaudit-0.3.0 → evalaudit-0.5.0}/pyproject.toml +1 -1
  21. evalaudit-0.5.0/tests/data/README.md +35 -0
  22. evalaudit-0.5.0/tests/data/make_mt_bench_csv.py +77 -0
  23. evalaudit-0.5.0/tests/data/mt_bench_gpt4_pair.csv +2401 -0
  24. evalaudit-0.5.0/tests/data/mt_bench_human.csv +3356 -0
  25. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_agreement.py +340 -33
  26. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_audit.py +2444 -1464
  27. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_compare.py +20 -4
  28. evalaudit-0.5.0/tests/test_judge.py +6182 -0
  29. evalaudit-0.5.0/tests/test_not_shown.py +367 -0
  30. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_pairwise.py +131 -4
  31. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_power.py +141 -21
  32. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_scores.py +56 -3
  33. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_version.py +84 -84
  34. evalaudit-0.3.0/CHANGELOG.md +0 -255
  35. evalaudit-0.3.0/tests/test_judge.py +0 -2395
  36. {evalaudit-0.3.0 → evalaudit-0.5.0}/.github/workflows/tests.yml +0 -0
  37. {evalaudit-0.3.0 → evalaudit-0.5.0}/.gitignore +0 -0
  38. {evalaudit-0.3.0 → evalaudit-0.5.0}/LICENSE +0 -0
  39. {evalaudit-0.3.0 → evalaudit-0.5.0}/evalaudit/__init__.py +0 -0
  40. {evalaudit-0.3.0 → evalaudit-0.5.0}/examples/.gitkeep +0 -0
  41. {evalaudit-0.3.0 → evalaudit-0.5.0}/examples/interval_method_study.py +0 -0
  42. {evalaudit-0.3.0 → evalaudit-0.5.0}/examples/pairwise_cluster_study.py +0 -0
  43. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/__init__.py +0 -0
  44. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_confidence.py +0 -0
  45. {evalaudit-0.3.0 → evalaudit-0.5.0}/tests/test_readme.py +0 -0
@@ -0,0 +1,840 @@
1
+ # Changelog
2
+
3
+ Every change to evalaudit that a caller can see is recorded here. The format
4
+ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
+ version numbers follow [semantic versioning](https://semver.org/). Before 1.0
6
+ a minor version can break things, and 0.3.0, 0.4.0 and 0.5.0 all do.
7
+
8
+ An entry marked **Breaking** changes what a call that worked on the previous
9
+ release returns, raises or prints. The mark goes on the entry wherever it
10
+ sits, under Changed, Fixed or Removed.
11
+
12
+ Every release is dated by the day PyPI received its upload, in UTC. 0.3.0 is
13
+ the first release with a tag, and 0.4.0 and 0.5.0 have one too. The entries
14
+ below 0.3.0 were reconstructed after the fact, and their contents were
15
+ checked against the published wheels and the commit each wheel matches.
16
+
17
+ ## [Unreleased]
18
+
19
+ ## [0.5.0] - 2026-09-26
20
+
21
+ `judge_validation` can now measure the judge against a second human. Most
22
+ of the rest is text. Several printed sentences change, most of them for
23
+ hyphens and plurals, and one `audit` finding changes severity. Figures
24
+ change in one case. In the both-orders design, `position_bias` now scores a
25
+ pair judged more than once in an order when its first two reversed
26
+ judgements held a tie and a later two did not. That moves the count of
27
+ pairs scored, the consistency rate and its interval, and the figures on the
28
+ flips. A pair judged once in each order scores as before. Anything that
29
+ matches on the old sentences will stop matching. Every entry was checked by
30
+ running the same calls against the v0.4.0 tag and against this tree.
31
+
32
+ ### Changed
33
+
34
+ - Nothing.
35
+
36
+ ### Added
37
+
38
+ - `judge_validation(..., item_ids=None, human_baseline=None, ties=None)`
39
+ measures the judge against a human baseline. `human_baseline` is a frame
40
+ with `item_id`, `cluster_id`, `rater_id` and `rating`, one row per human
41
+ rating, and `item_ids` names the item at each position of `human` and
42
+ `judge`. The result carries judge-human and human-human alpha on the same
43
+ items, and a percentile interval on judge-human minus human-human that
44
+ resamples whole clusters. The verdict reads that interval and nothing
45
+ else, and is called provisional when it rests on fewer than 30 clusters.
46
+ The interval is refused when fewer than 90% of resamples leave both
47
+ alphas defined.
48
+
49
+ `ties` is required with a baseline. `"category"` keeps a tie as a label of
50
+ its own and `"drop"` sets it aside as no label, and the choice applies to
51
+ every figure in the result, the headline included. Baseline items are set
52
+ aside by three rules in a fixed order, and the summary counts what each
53
+ one took. A baseline supports only `level="nominal"`. On the MT-Bench
54
+ human judgements the figures reproduce `analysis/mt-bench.md` to the third
55
+ decimal. The new arguments are refused, each with its own message, in the
56
+ combinations that cannot work, such as `ties` or `item_ids` without a
57
+ baseline. Missing values in `item_id`, `cluster_id`, `rater_id` or
58
+ `item_ids` are refused, and the message names the column, the count and
59
+ where the first one is. Every check on the baseline runs before anything
60
+ is resampled.
61
+
62
+ - `JudgeValidation.n_dropped_ties`, `JudgeValidation.ties`, eleven fields
63
+ that start with `baseline_`, and the properties `has_baseline` and
64
+ `has_baseline_interval`. The new fields are the last in the dataclass and
65
+ have defaults, so building a `JudgeValidation` by position with 0.4.0's
66
+ fields still works. A call that passes none of the three new arguments
67
+ returns the same numbers as 0.4.0.
68
+
69
+ ### Fixed
70
+
71
+ - **Breaking.** `JudgeValidation.summary()` said "No interval was computed,
72
+ so nothing here is placed against sampling error." when the resamples had
73
+ been drawn and too few of them left alpha defined. On three items, two of
74
+ them unanimous, with `n_boot=2000` and `seed=0`, it now reads "There is
75
+ no interval on that alpha. 591 of 2000 resamples left it undefined, above
76
+ the 10% this reports through. Those are the resamples where every label
77
+ was the same, so percentiles of the rest would understate the upper
78
+ bound." When one resample is left undefined it reads "That is the
79
+ resample where every label was the same", and when only one was drawn
80
+ the count reads "1 of 1 resample". With `n_boot=0` it still says no
81
+ interval was computed. The `audit` judge finding carries the new
82
+ sentence.
83
+
84
+ - **Breaking.** The `audit` finding on a margin whose interval clears zero
85
+ opened "This is the margin between new and old, and it survives the
86
+ fit." No model is fitted there. It now reads "This is the margin between
87
+ new and old, and its interval excludes zero."
88
+
89
+ - **Breaking.** A count or figure of one was printed with a plural noun, or
90
+ with a verb or pronoun that assumed more than one. Each now takes the
91
+ singular. Anything other than one prints as before.
92
+
93
+ - `ScoreCI.summary()`. "With only 1 observations" now reads "With only 1
94
+ observation", and "The interval spans 1 points." now reads "The
95
+ interval spans 1 point."
96
+ - `ComparisonResult.summary()`. "1 of 1 items changed between systems."
97
+ now reads "1 of 1 item changed between systems."
98
+ - `KappaResult.summary()`. "(1 items, 2 raters, 2 categories)" now reads
99
+ "(1 item, 2 raters, 2 categories)".
100
+ - `AgreementResult.summary()`. "1 of 1 items graded more than once" now
101
+ reads "1 of 1 item graded more than once". Where the interval is
102
+ refused, "1 of 1 resamples came back undefined" now reads "1 of 1
103
+ resample came back undefined", and after one undefined resample "Those
104
+ resamples are the unanimous ones" now reads "That resample is the
105
+ unanimous one".
106
+ - `BTResult.summary()`. With two models, "0 of 1 pairs is separable" now
107
+ reads "0 of 1 pair is separable". With one pair separable among
108
+ several, "1 of 3 pairs are separable" now reads "1 of 3 pairs is
109
+ separable", since the verb follows the separable count. Where the
110
+ interval is refused, "1 of 1 resamples came back undefined" now reads
111
+ "1 of 1 resample came back undefined".
112
+ - The `ValueError` from `bradley_terry` for a missing item id. "item_id is
113
+ missing on 1 of 1 comparisons" now reads "item_id is missing on 1 of 1
114
+ comparison".
115
+ - `PositionBias.summary()`, randomised design. "Position A won 100.0% of 1
116
+ judgements" now reads "of 1 judgement". "1 pairs were also run in the
117
+ reverse order" now reads "1 pair was also run in the reverse order".
118
+ "1 judgements were ties and are left out of the rates above." now reads
119
+ "1 judgement was a tie and is left out of the rates above."
120
+ - `PositionBias.summary()`, both orders. "1 of 1 pairs were run in both
121
+ orders" now reads "1 of 1 pair was run in both orders", and "1 of 2
122
+ pairs were run in both orders" now reads "1 of 2 pairs was run in both
123
+ orders", since the verb follows the pairs run both ways. "on 0.0% of 1
124
+ pairs" now reads "on 0.0% of 1 pair", and "Of the 1 pairs it flipped
125
+ on" now reads "Of the 1 pair it flipped on".
126
+ - `LengthBias.summary()`. "(1 pairs)" now reads "(1 pair)", and "(1
127
+ disagreements)" now reads "(1 disagreement)", with or without a fitted
128
+ model. A length step that prints as 1, "1 characters of extra length
129
+ multiplies" or "1 characters multiplies", now reads "1 character".
130
+ - `PowerResult.summary()`. "you need roughly 1 paired comparisons" now
131
+ reads "you need roughly 1 paired comparison", and "about 1 discordant
132
+ pairs, and McNemar reads only those." now reads "about 1 discordant
133
+ pair, and McNemar reads only that one." A difference that prints as 1,
134
+ "is 1 points" or "a difference of 1 points", now reads "1 point".
135
+
136
+ The `audit` compare, agreement, position, length and power findings carry
137
+ these sentences word for word.
138
+
139
+ - **Breaking.** Compound modifiers are hyphenated in printed text.
140
+
141
+ - `PowerResult.summary()`. "To detect a 5 point difference", "leaves at
142
+ most a 5 point difference" and "To find a 5 point difference" now read
143
+ "5-point", and the same holds for any figure. "the continuity
144
+ corrected and exact forms" now reads "continuity-corrected", and
145
+ "Pairing removes the item to item difficulty" now reads
146
+ "item-to-item". The `audit` power finding carries these sentences.
147
+ - The `audit` action under "Rater agreement holds up". "Keep the double
148
+ graded sample" now reads "Keep the double-graded sample".
149
+ - The `ValueError` from `detectable_effect` and `min_sample_size` when
150
+ power does not exceed alpha. "A two sided test" now reads "A two-sided
151
+ test".
152
+
153
+ - **Breaking.** `PositionBias.summary()` printed a rate, an interval and a
154
+ verdict when nothing could be scored.
155
+
156
+ - Randomised design. On three pairs judged once, each a tie, it printed
157
+ "Each pair was judged once, so this reports the position-A win rate.
158
+ Position A won nan% of 0 judgements (95% CI: nan% to nan%, exact
159
+ binomial p=nan). The interval includes 50%, so the data cannot show
160
+ that position moved the judge." and went on to the caveat about
161
+ randomised order. It now reads "Each pair was judged once, so this
162
+ would report the position-A win rate. Every judgement was a tie, so
163
+ there is no rate to report and nothing here tests position."
164
+ - Both orders. On two pairs run both ways, each with a tie on one side,
165
+ and one pair judged once, it printed "2 of 3 pairs were run in both
166
+ orders, so this reports the consistency rate. The judge named the same
167
+ output under both orderings on nan% of 0 pairs (95% CI: nan% to nan%).
168
+ It never flipped, so there is no direction to test and nothing here
169
+ points at position." It now reads "2 of 3 pairs were run in both
170
+ orders, so this would report the consistency rate. In every pair run
171
+ both ways, the judge called a tie every time in at least one of the two
172
+ orders, so no pair could be scored and there is no rate to report.
173
+ Nothing here tests position."
174
+
175
+ The randomised summary ended "3 judgements were ties and are left out of
176
+ the rates above." and the both-orders one ended the same way with 2.
177
+ There are no rates for the ties to be left out of, so that sentence is
178
+ gone from both. The `audit` position finding on this data was an info
179
+ finding titled "The data cannot show a position effect in the pairwise
180
+ judgements". It is now a warning titled "Position bias is undefined on
181
+ this data", and it carries the new summary.
182
+
183
+ - **Breaking.** In the both-orders design, `position_bias` formed each
184
+ pair's couple, its first two judgements that are reverses, before looking
185
+ at ties, and dropped the pair if either was a tie. A pair judged more than
186
+ once in an order was dropped for a tied first couple even when a later
187
+ couple had no tie, and the summary did not say so. Ties are now set aside
188
+ first, so the pair is scored on its first two decisive judgements that are
189
+ reverses. On two consistent pairs and a third judged x/y and y/x as ties
190
+ and then x/y for x and y/x for y, it printed "The judge named the same
191
+ output under both orderings on 100.0% of 2 pairs (95% CI: 34.2% to
192
+ 100.0%). It never flipped, so there is no direction to test and nothing
193
+ here points at position." It now reads "The judge named the same output
194
+ under both orderings on 66.7% of 3 pairs (95% CI: 20.8% to 93.9%). Of the
195
+ 1 pair it flipped on, 1 went to whichever output was shown first (100.0%,
196
+ 95% CI: 2.5% to 100.0%, exact binomial p=1.0000)." and goes on to say the
197
+ data cannot show that the flips have a direction. `n_pairs_scored` goes
198
+ from 2 to 3 and `n_decisive` from 0 to 1. A pair judged once in each
199
+ order scores as before, since setting its ties aside leaves a couple
200
+ exactly when neither judgement was a tie. The `audit` position finding
201
+ carries the new figures.
202
+
203
+ - The `rater_agreement` docstring said that without an interval nothing in
204
+ the result would name an outlier rater. Since 0.4.0 the summary lists
205
+ alpha with each rater left out and names no rater either way, and the
206
+ docstring now says so.
207
+
208
+ - The README said scipy computes McNemar's test. It is statsmodels. It also
209
+ said the package builds fifteen intervals, while its own lists named
210
+ seventeen. It now counts eighteen, with the interval on the human baseline
211
+ among the eight pinned against a reference.
212
+
213
+ ### Removed
214
+
215
+ - Nothing.
216
+
217
+ ## [0.4.0] - 2026-09-11
218
+
219
+ Nearly everything in 0.4.0 changes what a
220
+ summary or a report prints. One rule is behind it. A result that does not
221
+ clear its threshold has failed to show something, and it has not shown the
222
+ opposite. Summaries that described such a result as if the opposite were
223
+ shown, and verdicts decided on a point estimate where an interval existed,
224
+ now say what the data did and did not show. Several `audit` findings change
225
+ severity on data you have already run. Each breaking entry says what it
226
+ was, what it is now and what to do about it. Every entry was checked by
227
+ running the same calls against 0.3.0 from PyPI and against the code
228
+ released as 0.4.0.
229
+
230
+ ### Changed
231
+
232
+ - **Breaking.** `audit` reads rater agreement and judge agreement off the
233
+ interval on alpha. An interval wholly above the threshold keeps "Rater
234
+ agreement holds up" or "The judge tracks the humans", and one wholly below
235
+ it keeps the "below the working threshold" title. An interval running both
236
+ sides of the threshold, or no interval at all, is now a `warning` titled
237
+ "The data cannot show that rater agreement clears the working threshold"
238
+ or "The data cannot show that the judge clears the working threshold".
239
+ 0.3.0 decided on the point estimate. Ratings with alpha 0.730 and an
240
+ interval of 0.466 to 0.933 came back `info` as "Rater agreement holds up",
241
+ and so did any ratings audited with `n_boot=0`. Judge labels with alpha
242
+ 0.581 and an interval of 0.467 to 0.691 came back "Judge-human agreement
243
+ is below the working threshold". Rerun any audit you have sent. A finding
244
+ that moved from `info` to `warning` also changes the report's verdict.
245
+
246
+ - **Breaking.** Without an `effect_of_interest`, `audit` no longer sizes
247
+ power on the margin it measured. 0.3.0 made the observed margin the effect
248
+ at issue, as in "The effect at issue is 5.0 points, the margin this eval
249
+ reports." Power computed on an observed effect is a monotone function of
250
+ the p-value, so on a margin whose interval includes zero the finding came
251
+ out `critical` almost every time and repeated the comparison. The finding
252
+ now reports the design's reach, the smallest difference the eval could
253
+ find at the configured power, and makes no claim about the margin. It is
254
+ a `warning` titled "No effect of interest was stated, so this reports the
255
+ eval's reach", because without a stated effect the audit does not know
256
+ what size of difference matters. Its lead says "This reports what the
257
+ design could find and makes no claim about the margin it measured." On 400
258
+ paired items with a 5.0-point margin and an interval of 1.1% to 9.0%,
259
+ 0.3.0 opened the report with a `critical` power finding and the verdict
260
+ said the conclusion did not hold. Two systems with a margin of exactly
261
+ zero, which 0.3.0 skipped, get the same warning.
262
+
263
+ With one system and no stated effect the check is still skipped, and its
264
+ reason no longer offers the observed margin as a stand-in. It read
265
+ "Nothing to size the eval against. Pass effect_of_interest in the config,
266
+ or scores for two systems so the observed margin can stand in for it."
267
+ and now reads "No effect of interest was stated and there is no comparison
268
+ whose reach could be reported. Pass effect_of_interest in the config, or
269
+ scores for two systems."
270
+
271
+ An audit of two pass-rate systems with no stated effect now always
272
+ carries this warning, so its report never comes back with no warnings.
273
+ Set `effect_of_interest` to the difference the decision turns on.
274
+
275
+ - **Breaking.** With an `effect_of_interest` stated, `audit` puts a power
276
+ finding at `info` in two cases where 0.3.0 made it `critical`, because the
277
+ margin's interval already answers the question power asks.
278
+
279
+ - The margin's interval clears zero. The finding is titled "The margin is
280
+ established, and a tighter estimate would take more items". 0.3.0
281
+ titled it "This eval could not have detected the effect at issue".
282
+ - The margin's interval includes zero and stops short of the stated
283
+ effect in both directions. The finding is titled "The interval on the
284
+ margin rules out the effect at issue".
285
+
286
+ Rerun any audit whose report carried a power finding.
287
+
288
+ - **Breaking.** With an `effect_of_interest` stated, `audit` rewords the
289
+ power finding on a margin whose interval includes zero. Its title is now
290
+ "This eval had under 80% power for the effect at issue", with the
291
+ percentage taken from `power`, because a design
292
+ short of that power can still find the effect, only less often. Its action
293
+ no longer says "No conclusion about an effect this size can be drawn from
294
+ this data, in either direction. A null result here says the eval was too
295
+ small and says nothing about the systems." It reads the margin's interval
296
+ one side at a time and says one of three things. Either "The interval on
297
+ the margin runs past an effect this size in both directions, so the data
298
+ cannot show an effect this size and cannot rule one out.", or "The
299
+ interval on the margin rules out old ahead by this much, and it cannot
300
+ rule out new ahead by this much." with your system names, or for a single
301
+ system "No margin was measured here, so there is no interval to read
302
+ against an effect this size." Anything matching on the old title or action
303
+ will stop matching.
304
+
305
+ - **Breaking.** `AuditReport.summary()` rewords its verdict, which is the
306
+ first line of every report.
307
+
308
+ - With critical findings, "the conclusion this data is being asked to
309
+ support does not hold as stated." is now "this data cannot support the
310
+ conclusion as stated. That does not mean the conclusion is wrong."
311
+ - With warnings and no criticals, "The result stands and the design
312
+ weakens it, so report it with what the warning say attached." is now
313
+ "The checks that ran did not find a problem that leaves the conclusion
314
+ unsupported. That does not mean the eval is sound. Report the result
315
+ with what the warning says attached."
316
+ - With neither, "All 4 checks that ran came back clean, so the eval holds
317
+ up on everything this report could test." is now "The 3 checks that ran
318
+ did not find a problem in what they could test. That does not mean the
319
+ eval is sound." The number changed too. 0.3.0 counted findings, and the
320
+ score check gives one finding per system, so two systems made three
321
+ checks read as four. A single check reads "The one check that ran", and
322
+ a report where nothing could run reads "No check could run, so this
323
+ report has tested nothing."
324
+
325
+ Anything matching on the old verdicts will stop matching. The severity
326
+ counts are unchanged by this entry.
327
+
328
+ - **Breaking.** `audit` rewords several titles and actions.
329
+
330
+ - "No position effect in the pairwise judgements" is now "The data cannot
331
+ show a position effect in the pairwise judgements". Its action opens
332
+ "The data has not cleared the judge of position bias." where it opened
333
+ "Nothing to do here."
334
+ - "No length effect in the judge's choices" is now "The data cannot show
335
+ a length effect in the judge's choices". Its action "Nothing to do
336
+ here." is now "The data has not cleared the judge of rewarding length."
337
+ - An alpha that does not exist is titled "Rater agreement is undefined on
338
+ this data" or "Judge-human agreement is undefined on this data", and the
339
+ lead ends "and this data gives no alpha to hold against it." 0.3.0
340
+ titled it "Rater agreement is below the working threshold" or
341
+ "Judge-human agreement is below the working threshold", and said the
342
+ grades "reproduce less well than the 0.667".
343
+ - The action on a margin that clears zero said "The lower bound is the
344
+ size of the win the data actually supports." When the second system
345
+ leads, the lower bound is the far end of the interval. It now says "The
346
+ end of the interval nearer zero is the smallest margin this data
347
+ supports."
348
+ - The length lead "Two models run here and they point opposite ways." now
349
+ also needs the preference fit's interval to clear zero. 0.3.0 printed it
350
+ off the sign of a preference coefficient whose interval ran over zero,
351
+ and said the judge "picks the longer answer more often" beside an odds
352
+ ratio of 0.94 to 1.40. Such data now gets the plain lead for the
353
+ direction the finding reports.
354
+
355
+ - **Breaking.** Module summaries put every result that falls short of its
356
+ threshold in one form, "so the data cannot show that" followed by "That
357
+ does not mean".
358
+
359
+ - `ComparisonResult.summary()`. "The interval crosses zero, so the data
360
+ cannot confirm that either system is better." is now "The interval
361
+ includes zero, so the data cannot show that either system is better.
362
+ That does not mean the two are level."
363
+ - `PositionBias.summary()`, randomised design. "The interval covers 50%,
364
+ so the data cannot show that position moved the judge." is now "The
365
+ interval includes 50%, so the data cannot show that position moved the
366
+ judge. That does not mean the judge ignores position."
367
+ - `PositionBias.summary()`, both orders. "The flips split evenly across
368
+ the two positions, so this is an unsteady judge rather than a
369
+ position-biased one." is now "That share is not clear of 50% at this
370
+ many flips, so the data cannot show that the flips have a direction.
371
+ That does not mean the judge is free of position bias." 0.3.0 printed
372
+ the old sentence at 7 flips of 9 toward the answer shown first.
373
+ - `LengthBias.summary()`. "The interval covers no effect, so the data
374
+ cannot show that length moved the judge." is now "The interval includes
375
+ an odds ratio of 1, so the data cannot show that length moved the
376
+ judge. That does not mean length plays no part in its choices." For the
377
+ disagreement fit, "That interval covers no effect, so the judge does
378
+ not depart from the humans in the direction of length." is now "That
379
+ interval includes an odds ratio of 1, so the data cannot show that the
380
+ judge breaks with the humans toward longer or shorter answers. That does
381
+ not mean it follows them on length."
382
+ - `AgreementResult.summary()`. "so the data cannot distinguish the raters"
383
+ is now "so the data cannot show that any one rater is pulling alpha
384
+ down" where every leave-one-out alpha is undefined. Where there are
385
+ leave-one-out figures the summary no longer judges the raters at all,
386
+ as the entry on rater dropout below says.
387
+ - `JudgeValidation.summary()`. "the data cannot single out a slice" is now
388
+ "the data cannot show that the judge does worse on any one slice".
389
+ Where the intervals overlap it adds "That does not mean it does equally
390
+ well on all of them."
391
+
392
+ Anything matching on the old sentences will stop matching.
393
+
394
+ - **Breaking.** `AgreementResult.summary()` and `JudgeValidation.summary()`
395
+ name one of Krippendorff's bands only when the whole interval sits in it.
396
+ 0.3.0 named the band from the point estimate, with "That is at or above
397
+ 0.800, the conventional bar for treating coded data as reliable.", "That
398
+ sits between 0.667 and 0.800, which supports tentative conclusions and no
399
+ firm ones." and "That is below 0.667, the conventional floor for drawing
400
+ any conclusion from coded data." These now read "The whole interval sits
401
+ above 0.800", "The whole interval sits between 0.667 and 0.800" and "The
402
+ whole interval sits below 0.667". An interval running over 0.667 reads
403
+ "The interval runs both sides of 0.667, the conventional floor for drawing
404
+ any conclusion from coded data, so the data cannot show that rater
405
+ agreement clears it. That does not mean it falls short of it." In
406
+ `JudgeValidation` the subject is "the judge's agreement with the humans".
407
+ An interval that clears 0.667 and runs over 0.800 gets the same form at
408
+ 0.800, and a summary with no interval names no band. Judge labels with
409
+ alpha 0.801 and an interval of 0.601 to 0.951 printed "That is at or
410
+ above 0.800" in 0.3.0. Anything matching on the old sentences will stop
411
+ matching.
412
+
413
+ - **Breaking.** `AgreementResult.summary()` no longer names a rater. 0.3.0
414
+ named one when dropping them raised alpha by more than half the width of
415
+ the interval on alpha. That compares the shift with the sampling error on
416
+ alpha, and the shift has a sampling error of its own that can be much
417
+ larger. Across simulated evals with three raters who differed only by
418
+ chance, the rule named a rater in 4.2% of 500 when everyone graded every
419
+ item. When the third rater graded a fifth of the items it named one in
420
+ 32.0% of 500, always one of the two who graded everything. In one of those
421
+ evals 0.3.0 printed "Dropping r2 raises alpha to 0.878, a shift of 0.326
422
+ against a sampling error of 0.200." about a rater who graded all 60 items.
423
+ The same rule caught a rater wrong three times as often as the others in
424
+ 72.4% of 500.
425
+
426
+ The summary now lists alpha with each rater left out, as in "Leaving one
427
+ rater out at a time gives alpha of 0.878 without r2 (+0.326), 0.524
428
+ without r3 (-0.028), and 0.483 without r1 (-0.069).", and says "The
429
+ package does not judge whether any of these shifts is larger than noise,
430
+ so it names no rater. A rater near the top of this list is a lead to check
431
+ rather than a finding." It lists five raters at most and points to
432
+ `rater_dropout` for the rest. `top_dropout_rater` always returns None.
433
+ `dropout_is_distinguishable` still computes the old rule, its docstring
434
+ says why the rule was withdrawn, and nothing in the package reads it.
435
+
436
+ The `audit` agreement finding carries the new sentence. Its severity never
437
+ came from this rule, so no finding changes severity. If you acted on a
438
+ rater a summary named, check that rater by hand.
439
+
440
+ - **Breaking.** `PositionBias` puts the Clopper-Pearson interval, the exact
441
+ binomial interval, on the rate it tests against a half, in both designs,
442
+ and decides on it. The exact binomial p-value printed beside it comes from
443
+ the same test, so the two cannot disagree about 50%. 0.3.0 used a Wilson
444
+ interval in the randomised design. Wilson and the exact test disagree at
445
+ 50% in 188 of 20,300 (n, k) cells up to 200 judgements, and in every one
446
+ the Wilson interval clears 50% while the p-value does not. At 11 of 14 in
447
+ the randomised design 0.3.0 printed "(95% CI: 52.4% to 92.4%, exact
448
+ binomial p=0.0574)" and then "The interval clears 50%, so the judge
449
+ favours whichever output it sees first." It now prints "(95% CI: 49.2% to
450
+ 95.3%, exact binomial p=0.0574)" and "The interval includes 50%, so the
451
+ data cannot show that position moved the judge." Every randomised interval
452
+ moves, and a randomised verdict flips wherever the two used to disagree.
453
+
454
+ In the both-orders design 0.3.0 printed the share of flips with a bare
455
+ p-value, "(77.8%, exact binomial p=0.1797).", and decided on that p-value.
456
+ It now prints "(77.8%, 95% CI: 40.0% to 97.2%, exact binomial p=0.1797)."
457
+ and decides on the interval, which gives the same answer as the p-value in
458
+ every case. The consistency rate has no test beside it and keeps its
459
+ Wilson interval. Rerun any position check near the line.
460
+
461
+ ### Added
462
+
463
+ - `BTResult.n_missing_item_ids`, the number of comparisons with no item id.
464
+ It is the last field of the dataclass and defaults to 0, so building a
465
+ `BTResult` by position with 0.3.0's fields still works. `to_elo` carries
466
+ it across.
467
+ - `PositionBias.position_a_ci_low` and `PositionBias.position_a_ci_high`,
468
+ the Clopper-Pearson interval on `position_a_rate`. In the randomised
469
+ design they
470
+ equal `ci_low` and `ci_high`. They are the last two fields and default to
471
+ NaN, so building a `PositionBias` by position with 0.3.0's fields still
472
+ works. A both-orders result built that way has no interval, so its
473
+ `has_position_effect` is False.
474
+
475
+ ### Fixed
476
+
477
+ - **Breaking.** `KappaResult.summary()` said "Krippendorff's alpha does not
478
+ have that problem and handles missing data, so prefer rater_agreement for
479
+ anything you report." Alpha has the problem too. Two raters who agree on
480
+ 90 of 100 items score kappa 0.444 and alpha 0.447 when one category takes
481
+ 90% of the ratings, and 0.800 and 0.801 when both are used equally often.
482
+ It now says "Krippendorff's alpha moves the same way, because it also
483
+ corrects for chance using how often each category is used." and gives
484
+ missing ratings, more than two raters and ordered scales as the reasons to
485
+ prefer `rater_agreement`. The `cohens_kappa` docstring made the same claim
486
+ and now quotes the four figures.
487
+
488
+ - **Breaking.** `ScoreCI.summary()` told the reader to "treat differences
489
+ smaller than that as unresolved." A paired comparison resolves
490
+ differences far smaller than either score's own interval. The sentence
491
+ now reads "To compare this score with another, read the interval on the
492
+ difference, which compare_paired and compare_independent report. Two
493
+ score intervals that overlap do not show the systems are level." The
494
+ `scores` module docstring made the same claim and no longer does.
495
+
496
+ - **Breaking.** `PowerResult.summary()` from `detectable_effect` said
497
+ "anything smaller was out of reach before the first item was graded". A
498
+ difference below the reach can still reach significance, only less often.
499
+ It now says "A smaller difference could still reach significance here,
500
+ with a chance below 80%." Where no difference reaches the requested power,
501
+ "This eval could not have found anything, whatever the two systems really
502
+ do." and, for independent samples, "and this eval could not have found
503
+ anything." are now "A null result from this eval says little about the
504
+ systems." The `detectable_effect` docstring said "An eval could not have
505
+ found anything smaller" and now says "No smaller difference reaches the
506
+ requested power".
507
+
508
+ - **Breaking.** `JudgeValidation.summary()` on a single item said "Every
509
+ label that could be compared was identical" whatever the labels were,
510
+ beside a plain accuracy of 0.0% when they differed, and counted "1 items".
511
+ It now reads "(nominal, 1 item). One item cannot carry a reliability
512
+ estimate, so there is no number to report and no interval around it." The
513
+ count of items set aside reads "1 item was set aside" where it read "1
514
+ items were set aside".
515
+
516
+ - **Breaking.** `BTResult.summary()` under `resample="comparisons"` said
517
+ "these 240 comparisons share 239 items" when one comparison had no item
518
+ id and no two shared anything. Sharing is now counted among the
519
+ comparisons that carry an id. When none share, the sentence is left out.
520
+ When some share and ids are missing, it reads "the 239 comparisons that
521
+ carry an item id share 26 items".
522
+
523
+ - The README example and the prose under it show the new wording.
524
+
525
+ ### Removed
526
+
527
+ - Nothing.
528
+
529
+ ### Known limitations
530
+
531
+ This rule is unchanged since 0.3.0 and is not fixed in this release. The
532
+ figures come from simulations run against 0.3.0 and against the code released
533
+ as 0.4.0, which give the same results.
534
+
535
+ - `JudgeValidation` names a slice, and `audit` raises a critical finding on
536
+ it, only when the slice's own interval sits wholly below the interval on
537
+ the overall figure. That reads a difference off two separate intervals
538
+ instead of an interval on the difference, and it is conservative. With
539
+ five slices of 60 items that differ only by chance, it named a slice in
540
+ 0.5% of 200 simulated evals. It misses real differences. A slice where the
541
+ judge flipped 25% of the labels, against 10% in the other four, was named
542
+ in 32.5% of 200. When the summary says the data cannot show that the
543
+ judge does worse on any one slice, this rule is what did not show it.
544
+
545
+ ## [0.3.0] - 2026-09-10
546
+
547
+ 0.3.0 changes what `bradley_terry` reports on data you have already run, and
548
+ it turns several calls that used to return numbers into errors. Each
549
+ breaking entry says what it was, what it is now and what to do about it.
550
+
551
+ ### Changed
552
+
553
+ - **Breaking.** `bradley_terry` reads separability off the interval on the
554
+ gap between two ratings. Before, a pair was separable when its two rating
555
+ intervals did not overlap. Now a pair is separable when the percentile
556
+ interval on the difference between the two ratings excludes zero. Two
557
+ rating intervals can overlap while the gap between them is well measured,
558
+ because both ratings carry the error of the field average they are
559
+ measured against and that error cancels in the gap. So the old test
560
+ undercounted the pairs the data orders, and the pairs it left out were
561
+ never shown to be unordered. There is no option to bring the old rule
562
+ back. `n_separable` and `separable_pairs` can change on data you have
563
+ already run. On the README example they go from 5 of 15 pairs to 7. On
564
+ twelve simulated leaderboards where every item carried one comparison,
565
+ the count went up on five and stayed the same on seven. Nothing changes
566
+ in your code. Rerun, and replace any separability count you have
567
+ published.
568
+
569
+ - **Breaking.** `separable_pairs` has two new columns, `ci_low` and
570
+ `ci_high`, the interval on `difference`. They sit between `difference` and
571
+ `p_a_beats_b`, so code that reads columns by position will read the wrong
572
+ ones. Code that selects columns by name is unaffected.
573
+
574
+ - **Breaking.** `bradley_terry` resamples items by default. Before, the
575
+ bootstrap drew single comparisons, which treats judgements of the same
576
+ prompt as independent. Now it draws item ids with replacement and takes
577
+ every comparison made on each drawn item.
578
+
579
+ Where every item carries one comparison the default draws the same
580
+ resamples from the same seed as 0.2.1, and the ratings, their intervals
581
+ and the win matrix come out identical. Separability can still change,
582
+ for the reason in the entry above.
583
+
584
+ Where items carry several comparisons the intervals widen and fewer pairs
585
+ may separate. On a simulated board of 60 items with 20 comparisons each,
586
+ the rating intervals came out 1.1 to 1.5 times as wide as 0.2.1's. On
587
+ simulated data shaped like MT-Bench's human judgements, the rating
588
+ intervals 0.2.1 reported covered 88% of the time at a nominal 95% once
589
+ prompts shifted model strength by a spread of 0.5 on the log-odds scale,
590
+ and 80% at a spread of 1.0. `examples/pairwise_cluster_study.py` has the
591
+ table for 0.3.0.
592
+
593
+ To get the ratings and rating intervals an earlier version gave on the
594
+ same data and seed, pass `resample="comparisons"`. They come out
595
+ identical to 0.2.1's. `separable_pairs`, `n_separable` and the summary
596
+ follow the 0.3.0 rules, so those can differ.
597
+
598
+ - **Breaking.** `bradley_terry` raises `ValueError` in two cases that 0.2.1
599
+ accepted. Both apply only when `n_boot` is above zero and `resample` is
600
+ left at its default of `"items"`.
601
+
602
+ - Some row has no `item_id`. The item bootstrap has to know which item
603
+ each comparison belongs to.
604
+ - Every row has the same `item_id`, an empty string included. With one
605
+ item, every resample is the full data and every interval would have
606
+ width zero.
607
+
608
+ This is the change most likely to break a working script, so here is what
609
+ to do. Earlier versions required `item_id` and read it for nothing but a
610
+ count, so a constant, a blank or the row number got past the check and
611
+ changed nothing. Look at what yours holds.
612
+
613
+ - It names the prompt each comparison was made on. Fill in any gaps and
614
+ change nothing else. Your intervals now account for comparisons sharing
615
+ a prompt, which is the reason for the change.
616
+ - It is the row number, or anything else unique per row. Nothing raises,
617
+ and the ratings and their intervals come out as they did in 0.2.1.
618
+ - It is a constant or a blank. Record the prompt if you can. If you
619
+ cannot, pass `resample="comparisons"` to keep the intervals you had.
620
+ They come out identical to 0.2.1's on the same seed. They treat every
621
+ judgement as independent and run narrow wherever prompts carry several.
622
+ The summary says so when the column holds a constant or an empty
623
+ string. It says nothing when every `item_id` is missing, since then
624
+ nothing records which comparisons share a prompt.
625
+
626
+ With `n_boot=0` no resampling happens and any `item_id` is accepted.
627
+
628
+ - **Breaking.** `score_ci(method="t")` raises `ValueError` on three kinds of
629
+ input it used to return numbers for. What to do depends on the input.
630
+
631
+ - 0/1 data, including all passes, all fails and a single pass or fail.
632
+ The interval could run past 100%, as 49 passes in 50 did at 94.0% to
633
+ 102.0%, come back with width zero on all passes or all fails, or come
634
+ back as NaN on a single pass. Use `method="wilson"`, which
635
+ `method="auto"` already picks for 0/1 data and which works on a single
636
+ item.
637
+ - A single score that is not 0 or 1. It came back as NaN bounds behind two
638
+ RuntimeWarnings. One score has no spread, and the bootstrap returns
639
+ width zero on it too. Score more items.
640
+ - Continuous scores that are all the same. The interval came back with
641
+ width zero, which claims the observations fixed the mean exactly. Check
642
+ that the scorer is not returning a constant.
643
+
644
+ - **Breaking.** `score_ci(method="wilson")` raises `ValueError` on scores
645
+ that are not 0 or 1. Wilson counts passes, and on other scores it took the
646
+ count by truncating the sum of the scores. Ten scores of 0.99 printed a
647
+ mean of 0.990 beside an interval of 0.596 to 0.982. For scores that vary,
648
+ use `method="bootstrap"`, the interval on a mean, which `method="auto"`
649
+ already picks. Scores that are all the same, like that example, get width
650
+ zero from the bootstrap too, and 0.3.0 does not refuse that.
651
+
652
+ - **Breaking.** `score_ci`, `compare_paired` and `compare_independent`
653
+ raise `ValueError` for a confidence level that is not strictly between 0
654
+ and 1, with the message `confidence must be in (0, 1), got ...` that
655
+ `bradley_terry`, `rater_agreement`, `judge_validation` and `audit` already
656
+ used. Before, what came back at `confidence=1.5` depended on the method.
657
+ The bootstrap in all three raised from inside numpy. Wilson and both t
658
+ intervals returned NaN bounds. The two score intervals returned bounds
659
+ clipped to -100% and 100%. The signed-rank interval in `compare_paired`
660
+ returned an ordinary-looking interval under a "150% CI" label, which is
661
+ the case nothing would have flagged. Pass a level strictly between 0 and
662
+ 1.
663
+
664
+ - **Breaking.** Several summary strings are reworded, so any test or
665
+ pipeline that matches on them will fail.
666
+
667
+ - `BTResult.summary()` defines separable as "the interval on the gap
668
+ between the two ratings excludes zero". For pairs that do not separate
669
+ it says the data does not establish an order for them, that this does
670
+ not mean the models are level, and that more comparisons could separate
671
+ them. The phrases "meaning their intervals do not overlap", "cannot be
672
+ ordered", "an order the data does not carry" and "a ranking of noise"
673
+ are gone.
674
+ - `BTResult.summary()` adds a sentence when `resample="comparisons"` was
675
+ used on data where items carry several comparisons.
676
+ - `AgreementResult.summary()` for a scale nobody varied now reads "This
677
+ comes from a scale nobody varied. It is not perfect agreement." where it
678
+ read "That is not perfect agreement, it is a scale nobody varied."
679
+ - `JudgeValidation.summary()` for a rubric with one label now reads "This
680
+ comes from a rubric with one label in it. It is not perfect agreement."
681
+ where it read "That is not perfect agreement, it is a rubric with one
682
+ label in it."
683
+
684
+ - The Development Status classifier moved from Alpha to Beta.
685
+
686
+ ### Added
687
+
688
+ - `bradley_terry(..., resample="items")`, with `"comparisons"` as the other
689
+ choice.
690
+ - `BTResult.pairs`, one row per pair of models with the gap, the interval on
691
+ the gap and a `separable` column. `separable_pairs` is its separable rows.
692
+ - `BTResult.resample`, recording which unit the bootstrap drew. It and
693
+ `pairs` are the last two fields of the dataclass, so building a
694
+ `BTResult` by position with 0.2.1's fields still works.
695
+ - `to_elo` stretches the gap intervals with the gaps and carries `pairs` and
696
+ `resample` across.
697
+ - `examples/pairwise_cluster_study.py`, in the source distribution, which
698
+ measures how narrow the comparison bootstrap runs on MT-Bench-shaped data.
699
+
700
+ ### Fixed
701
+
702
+ - The `scores` module docstring said the interval at n=50 is roughly plus or
703
+ minus 10 points. It is about 10 at an 85% pass rate and about 13 at 50%.
704
+ - The README said every interval in the package is tested by simulation.
705
+ Several were not. It now says which intervals are tested by simulation,
706
+ which are checked against a reference implementation instead, and which
707
+ one is checked by neither.
708
+ - The README said `audit` runs five checks. It runs seven, drawn from five
709
+ modules.
710
+
711
+ ### Removed
712
+
713
+ - `AUDIT.md`, an internal review of the package, is no longer tracked, so
714
+ it is not in the source distribution.
715
+
716
+ ## [0.2.1] - 2026-09-08
717
+
718
+ Reconstructed after the fact. The published files match commit `809a650`.
719
+
720
+ ### Changed
721
+
722
+ - `__version__` is read from the installed package metadata. 0.0.1 and
723
+ 0.2.0 both reported `__version__` as 0.1.0, because a literal in
724
+ `__init__.py` had drifted from `pyproject.toml`.
725
+
726
+ ### Added
727
+
728
+ - `AUDIT.md`, an internal review of the package, in the source
729
+ distribution.
730
+
731
+ ### Fixed
732
+
733
+ - **Breaking.** The length-bias check read every coefficient as a
734
+ preference for long answers. In 0.2.0 a judge that preferred shorter
735
+ answers got the `audit` title "The judge is pulled by how long the answer
736
+ is", and `LengthBias.summary()` printed "A positive coefficient here is
737
+ not bias on its own" beside a negative coefficient. The `audit` finding
738
+ now takes its title, lead, action and verdict from the fit that decides,
739
+ with its own lead for when the two fits point opposite ways, and
740
+ `LengthBias.summary()` has its own sentence for a negative coefficient.
741
+ Anything matching on the old title or sentence will stop matching.
742
+
743
+ ### Removed
744
+
745
+ - Nothing.
746
+
747
+ ## [0.2.0] - 2026-09-07
748
+
749
+ Reconstructed after the fact. The wheel matches commit `242e354`. It was
750
+ uploaded twelve seconds before that commit was made, so it was built from
751
+ the working tree the commit then recorded.
752
+
753
+ ### Changed
754
+
755
+ - The project URLs point at github.com/mokayahezron/evalaudit. 0.1.0 pointed
756
+ at github.com/ondibahezron-glitch/evalaudit.
757
+ - `choix` is a development dependency on Python 3.10 and later, used to
758
+ check the Bradley-Terry fit.
759
+
760
+ ### Added
761
+
762
+ - The `pairwise` module, with `bradley_terry`, `to_elo` and `BTResult`.
763
+ Ratings come with bootstrap intervals and a count of separable pairs.
764
+ Models that fall into groups that never met, and models that never lost
765
+ or never won, get no ratings and a summary naming them. Ties are split or
766
+ dropped, and `ties="davidson"` is refused by name.
767
+ - The README code blocks are run by the test suite and checked against the
768
+ output they show.
769
+
770
+ ### Fixed
771
+
772
+ - Nothing.
773
+
774
+ ### Removed
775
+
776
+ - Nothing.
777
+
778
+ ## [0.1.0] - 2026-09-07
779
+
780
+ Reconstructed after the fact. The wheel matches commit `c1230f1`.
781
+
782
+ ### Changed
783
+
784
+ - Nothing.
785
+
786
+ ### Added
787
+
788
+ - `compare_paired` and `compare_independent`. McNemar with Tango's score
789
+ interval for paired binary data, the paired bootstrap, and the signed-rank
790
+ test with the Hodges-Lehmann interval. The score interval, the bootstrap
791
+ and Welch's t for independent samples.
792
+ - `rater_agreement`, Krippendorff's alpha with a bootstrap interval, an item
793
+ disagreement table and a rater dropout table. `cohens_kappa` and
794
+ `fleiss_kappa`.
795
+ - `judge_validation`, `position_bias` and `length_bias`.
796
+ - `detectable_effect` and `min_sample_size`.
797
+ - `audit`, which runs every check the supplied data supports and ranks the
798
+ findings by severity.
799
+ - The result types are exported from the package, `ScoreCI` among them.
800
+
801
+ ### Fixed
802
+
803
+ - Nothing.
804
+
805
+ ### Removed
806
+
807
+ - Nothing.
808
+
809
+ ## [0.0.1] - 2026-09-04
810
+
811
+ Reconstructed after the fact. The wheel's package files match commits
812
+ `aa8ad97`, `8a4cab0` and `b090c66`, which do not differ in them. Its version
813
+ number first appears in `pyproject.toml` at `b090c66`, committed about an
814
+ hour after the upload. It reported `__version__` as 0.1.0.
815
+
816
+ ### Changed
817
+
818
+ - Nothing.
819
+
820
+ ### Added
821
+
822
+ - `score_ci`, with Wilson, bootstrap and Student-t intervals, and a default
823
+ that picks Wilson for 0/1 data and the bootstrap otherwise.
824
+
825
+ ### Fixed
826
+
827
+ - Nothing.
828
+
829
+ ### Removed
830
+
831
+ - Nothing.
832
+
833
+ [Unreleased]: https://github.com/mokayahezron/evalaudit/compare/v0.5.0...main
834
+ [0.5.0]: https://github.com/mokayahezron/evalaudit/compare/v0.4.0...v0.5.0
835
+ [0.4.0]: https://github.com/mokayahezron/evalaudit/compare/v0.3.0...v0.4.0
836
+ [0.3.0]: https://github.com/mokayahezron/evalaudit/compare/809a650...v0.3.0
837
+ [0.2.1]: https://github.com/mokayahezron/evalaudit/compare/242e354...809a650
838
+ [0.2.0]: https://github.com/mokayahezron/evalaudit/compare/c1230f1...242e354
839
+ [0.1.0]: https://github.com/mokayahezron/evalaudit/compare/b090c66...c1230f1
840
+ [0.0.1]: https://github.com/mokayahezron/evalaudit/tree/b090c66