autorate 0.1.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 (47) hide show
  1. autorate-0.1.0/.gitignore +13 -0
  2. autorate-0.1.0/LICENSE +202 -0
  3. autorate-0.1.0/PKG-INFO +417 -0
  4. autorate-0.1.0/README.md +404 -0
  5. autorate-0.1.0/data/cert_jev.json +625 -0
  6. autorate-0.1.0/data/cert_laya.json +626 -0
  7. autorate-0.1.0/data/cert_massive_de_laya.json +3335 -0
  8. autorate-0.1.0/data/cert_massive_en_laya.json +3382 -0
  9. autorate-0.1.0/data/cert_massive_km_laya.json +3395 -0
  10. autorate-0.1.0/data/cert_massive_sw_laya.json +3369 -0
  11. autorate-0.1.0/data/cert_severity_ordinal.json +1258 -0
  12. autorate-0.1.0/data/contamination_probe.json +144 -0
  13. autorate-0.1.0/data/g1_feasibility.json +358 -0
  14. autorate-0.1.0/data/inventory.md +63 -0
  15. autorate-0.1.0/data/inventory.md.json +216 -0
  16. autorate-0.1.0/data/massive_provenance.json +386 -0
  17. autorate-0.1.0/data/pin_app_benchmark.json +274 -0
  18. autorate-0.1.0/data/resplit_200.json +18324 -0
  19. autorate-0.1.0/data/slices_language.json +42725 -0
  20. autorate-0.1.0/protocol.json +206 -0
  21. autorate-0.1.0/pyproject.toml +47 -0
  22. autorate-0.1.0/src/autorate/__init__.py +33 -0
  23. autorate-0.1.0/src/autorate/audit.py +1076 -0
  24. autorate-0.1.0/src/autorate/case_count.py +141 -0
  25. autorate-0.1.0/src/autorate/certificate.py +444 -0
  26. autorate-0.1.0/src/autorate/certify_choice.py +677 -0
  27. autorate-0.1.0/src/autorate/certify_score.py +377 -0
  28. autorate-0.1.0/src/autorate/certify_threshold.py +435 -0
  29. autorate-0.1.0/src/autorate/cli.py +1299 -0
  30. autorate-0.1.0/src/autorate/conformal.py +53 -0
  31. autorate-0.1.0/src/autorate/contamination.py +585 -0
  32. autorate-0.1.0/src/autorate/correction.py +500 -0
  33. autorate-0.1.0/src/autorate/drift.py +535 -0
  34. autorate-0.1.0/src/autorate/gate.py +99 -0
  35. autorate-0.1.0/src/autorate/groups.py +229 -0
  36. autorate-0.1.0/src/autorate/inventory.py +783 -0
  37. autorate-0.1.0/src/autorate/nonconformity.py +23 -0
  38. autorate-0.1.0/src/autorate/ordinal.py +188 -0
  39. autorate-0.1.0/src/autorate/protocol.py +771 -0
  40. autorate-0.1.0/src/autorate/readers/__init__.py +1 -0
  41. autorate-0.1.0/src/autorate/readers/feishu_zh.py +387 -0
  42. autorate-0.1.0/src/autorate/records.py +238 -0
  43. autorate-0.1.0/src/autorate/report.py +2246 -0
  44. autorate-0.1.0/src/autorate/scales.py +207 -0
  45. autorate-0.1.0/src/autorate/severity.py +969 -0
  46. autorate-0.1.0/src/autorate/tails.py +102 -0
  47. autorate-0.1.0/src/autorate/techreport.py +739 -0
@@ -0,0 +1,13 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ cert.json
7
+
8
+ # The composite action's badge output. The *committed* badge lives in `.github/badges/`; this is the
9
+ # per-run copy a workflow uploads as an artefact.
10
+ .autorate/
11
+
12
+ # SDD implementation worktrees (isolated lanes); never part of the published tree.
13
+ .worktrees/
autorate-0.1.0/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 the autorate authors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,417 @@
1
+ Metadata-Version: 2.5
2
+ Name: autorate
3
+ Version: 0.1.0
4
+ Summary: Certified automation rates for decision models: confidence is not permission.
5
+ License: Apache-2.0
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: numpy>=1.24
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=8; extra == 'dev'
11
+ Requires-Dist: ruff>=0.6; extra == 'dev'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # autorate
15
+
16
+ [![autorate gate](.github/badges/autorate-badge.svg)](.github/badges/autorate-badge.json)
17
+
18
+ Certified automation rates for decision models: confidence is not permission.
19
+
20
+ A benchmark answers "how good is the model". A certificate answers a question an
21
+ operator can actually be held to: "what share of this model's decisions may run
22
+ unattended while keeping the probability that an automated decision is wrong at most
23
+ `beta`, given the held-out data we really have". When the data cannot support that
24
+ claim, `autorate` prints `NOT_CERTIFIABLE` with a reason code, the observation at the
25
+ requested coverage that caused it, and a diagnostic sweep of the neighbouring coverage
26
+ levels -- evidence for the refusal, not a certificate. It exits 3 and does not publish a
27
+ number about nothing.
28
+
29
+ ## Verification-first navigation
30
+
31
+ - [Verification status](docs/VERIFICATION.md) · [Known errata](docs/ERRATA.md) · [Results](docs/RESULTS.md)
32
+ - The present implementation covers the decision-certification primitives described below; planned certificate families and guarantees are **not** implemented claims.
33
+ - This is a public repository. No public licensing or release rights are implied.
34
+
35
+ ## What a certificate carries
36
+
37
+ Stored: `feasible`, `reason`, `backend`, `method`, `alpha`, `beta`, `delta`, `autorate`,
38
+ `autorate_ci`, `conditional_error_bound`, `marginal_error_bound`, `mean_set_size`,
39
+ `qhat`, `n_cal`, `n_eval`, `envelope`, `meta`.
40
+
41
+ Derived, never stored, because each is a pure function of a stored field -- storing them
42
+ gave one number two owners and a guard that could not see the disagreement: `refusal_rate`
43
+ = `1 - autorate` (every evaluated case is empty, singleton, or multi-label) and `level`
44
+ = `1 - N_CLAIMS * delta` (three bounds are published per certificate).
45
+
46
+ Two of those rows carry the whole argument:
47
+
48
+ - `conditional_error_bound` is `P(wrong | automated) <= beta`. `marginal_error_bound`
49
+ is `P(wrong AND automated)`. They are different quantities, and a paper or a dashboard
50
+ that quotes one while meaning the other is the failure mode this tool exists to stop.
51
+ - Both are over **cases**, not archive rows. Repeats of one case are one observation, and on this
52
+ corpus they are not even evenly counted: laya's archived choice slice is 64 cases x 3 rows (192
53
+ rows), while jev's is 191 rows over the same 64 cases -- one case carries two rows, not three -- and
54
+ a jev case carries between one and three *distinct* probability vectors. What a repeat shares is the
55
+ measurement, not the row: laya's three repeats per case carry byte-identical probability vectors,
56
+ scores and gold labels (not identical rows: the repeat counter and its own elapsed time differ),
57
+ while jev's repeats need not carry the same vector at all -- so the `n` in every bound counts cases
58
+ and the record counts are published beside it instead of standing in for it.
59
+ - `envelope` is the machine-readable statement of what the bound assumed -- coverage
60
+ level, group conditioning, drift, and how empty sets were counted. A certificate read
61
+ without its envelope is invalid output, so `certify`, `compare` and `gate` all print it.
62
+ - `alpha` is the coverage floor the certificate is held to, not a label on it. An earlier version
63
+ swept a grid of coverage levels for the largest automation rate and tested the winner on
64
+ the same evaluation half, then printed "at alpha=0.050" while operating at 0.89
65
+ coverage (fixed in `241e395`). The pre-fix quantity is still checkable rather than asserted:
66
+ `data/cert_*.json` carries `meta.diagnostic_curve`, the sweep of coverage levels the old code
67
+ maximised over, and `meta.closest_attempt`, the point that came closest to the budget.
68
+ Selecting on the test half is snooping, so the set size now comes from the
69
+ calibration half at exactly the requested `alpha`; the grid survives only as diagnostic
70
+ evidence attached to a refusal.
71
+
72
+ `refusal_rate` and `mean_set_size` exist because an empty certified set is a refusal,
73
+ not a free correct answer: counting it as automation would inflate the rate.
74
+
75
+ ## Installing the wheel, and what it cannot do
76
+
77
+ `pip install autorate` installs the library and the console script. It does **not** install this
78
+ repository's evidence bundle. `autorate verify` re-derives *this* tree's published numbers, so it
79
+ needs `protocol.json` and the committed `data/` set and refuses with exit `4` when an artefact the
80
+ tables declare is absent -- by design, because the six `data/records_*.jsonl` files carry dataset
81
+ rows (labels, per-option probabilities, gold answers) and are not put in a package that any index
82
+ can host. For the evidence, take the repository, not the wheel:
83
+
84
+ ```bash
85
+ uv add "autorate @ git+https://github.com/Jackxiaozhiren/autorate"
86
+ ```
87
+
88
+ ## Commands
89
+
90
+ ```bash
91
+ uv sync --frozen --extra dev
92
+
93
+ uv run --frozen autorate snapshot --out data/inventory.md # corpus -> committed inventory
94
+ uv run --frozen autorate export-feishu --out data/records_feishu.jsonl
95
+ uv run --frozen autorate certify data/records_feishu.jsonl --backend laya --out cert.json
96
+ uv run --frozen autorate compare data/cert_laya.json data/cert_jev.json
97
+ uv run --frozen autorate gate data/records_feishu.jsonl --cert data/cert_jev.json --limit 5
98
+ uv run --frozen autorate report --out docs/RESULTS.md # write the published numbers
99
+ uv run --frozen autorate report --full # write docs/REPORT.md, one document for a reviewer
100
+ uv run --frozen autorate verify # re-derive them, fail on drift
101
+ ```
102
+
103
+ The `uv run --frozen` prefix is not decoration: the console script is not on `PATH`
104
+ after `uv sync`, and a bare `pytest` on this machine resolves to a different
105
+ interpreter entirely.
106
+
107
+ Exit codes: `0` certified, `3` `NOT_CERTIFIABLE`, `2` usage error -- an input this
108
+ command was handed is wrong, whether you named its path or a default supplied it --
109
+ `4` the committed state cannot reproduce what it publishes: a number that disagrees,
110
+ or a published artefact this repository's own tables declare that this run cannot
111
+ read -- because nothing is at its path, because the path itself refuses the open, or
112
+ because what is there is not the artefact that table publishes: a corrupt certificate,
113
+ a records line that is not a record, a document that is not text, a certificate stored
114
+ in a shape this build does not read, a committed inventory snapshot that will not load
115
+ or is not the mapping its renderers index.
116
+ One table-declared path stays `2` by ruling rather than oversight:
117
+ `data/records_feishu.jsonl` is the default every command reads -- `verify` and `report`
118
+ with no flag, `audit --records` with one -- absent or unreadable alike, and a default is
119
+ something its operator can name and repair.
120
+ A missing `data/inventory.md.json` alongside no corpus is `2` for a reason the refusal
121
+ does not cover -- there is nothing to verify against, which is a different statement
122
+ from a published artefact that cannot be read -- while a snapshot that is present and
123
+ will not load is `4` like any other published artefact.
124
+
125
+ `snapshot` and `export-feishu` read the corpus: pass `--root`, set
126
+ `AUTORATE_FEISHU_ROOT`, or place the checkout as a sibling of this repo
127
+ (`<workspace>/laya*/research/benchmarks/feishu_zh`). No source file carries an absolute
128
+ machine path, and `tests/test_cli.py` fails the build if one reappears -- a literal like
129
+ that passes every local gate and only breaks in the environment nobody can see.
130
+
131
+ Everything else runs offline on committed files; no command reaches the network.
132
+
133
+ ## GitHub Action and badge
134
+
135
+ [`action.yml`](action.yml) is a composite action. It shells out to the CLI and implements **no**
136
+ statistics: no bound, rate or aggregate is computed in the action or in
137
+ [`tools/action_report.py`](tools/action_report.py), which only maps the CLI's own exit code to a state and
138
+ formats what the CLI printed. The one number it parses is the denominator out of the tool's
139
+ `re-derived N /M values` line, so the badge can carry it instead of standing alone.
140
+
141
+ | input | default | meaning |
142
+ |---|---|---|
143
+ | `version` | empty | run `autorate==<version>` from PyPI through `uvx` |
144
+ | `ref` | empty | run the CLI from a detached worktree at that ref of this repository (needs full history) |
145
+ | `alpha`, `beta` | empty | passed to `certify`; empty means "use the frozen protocol's rubric" |
146
+ | `path` | `protocol.json` | read from the **working tree**, never `git show <rev>:protocol.json` |
147
+ | `records` | empty | when set, `certify` runs too, and its exit 3 is the verdict |
148
+ | `fail_on` | `drift,not-run` | states that fail the job |
149
+ | `badge_dir` | `.autorate` | where the `.svg` and its `.json` record are written |
150
+ | `comment` | `true` | post a PR comment quoting `autorate compare` verbatim (both denominators, no bound diff) |
151
+
152
+ The states are a closed vocabulary, and none of them is a boolean:
153
+
154
+ | state | when | what it does **not** mean |
155
+ |---|---|---|
156
+ | `certified` | `verify` exited 0 (and `certify` exited 0, if a records file was given) | not "the model is certified": `protocol.json`'s `claims[]` is where a certificate claim is made or refused |
157
+ | `not-certifiable` | `certify` exited 3 | not a broken pipeline -- a refusal is a result, and the badge says so |
158
+ | `drift` | `verify` exited 4 | not "the numbers moved slightly": the tree cannot reproduce what it publishes |
159
+ | `not-run` | no verdict was produced -- no CLI, no network, a usage error, a shallow checkout for `ref`, or a missing denominator line | never a pass |
160
+
161
+ `fail_on` defaults to `drift,not-run`: exit 4 fails the job, exit 3 is reported rather than red, and a run
162
+ that produced no verdict is never green. Add `not-certifiable` to make a refusal fail the job as well.
163
+
164
+ **What the PR comment is, and is not.** F8 rule 4 asks for a bound-versus-previous-bound diff with both
165
+ denominators. The shipped `autorate compare` prints each side's own `n_cal` / `n_eval` but states in its
166
+ own source that it "has no authority to pair anything", and returns a usage error rather than a
167
+ before/after table when the two certificates were frozen under different protocols. The comment therefore
168
+ quotes the command's output verbatim, carries both denominators as the tool printed them, and says plainly
169
+ that it computes no bound; claiming a bound diff would assert a capability the tool does not have
170
+ (PRD §13.3 item 4). When no certificate under `data/` changed — the usual case here — the comment says
171
+ `not-run`, and a comment that cannot be delivered is a named `not-run` in the job summary rather than a
172
+ silent stderr line. The literal rule 4 wording is recorded as a deviation in the milestone report.
173
+
174
+ Two things about checkouts, because both have already broken this repository's CI once:
175
+
176
+ - Every checkout in a workflow that uses the action sets `fetch-depth: 0`
177
+ ([`.github/workflows/action-gate.yml`](.github/workflows/action-gate.yml)). A shallow runner keeps one
178
+ commit in its object database, so `git show <rev>:<path>` fails there while passing in every real clone.
179
+ The action reads `protocol.json` from the working tree and states, in the job summary, which store it
180
+ read the *base revision* from: `history source: object-db` (full history), `shallow`, or `no-object-db`.
181
+ `tests/test_action_f8.py` refuses both a workflow that drops the flag and a summary that stops saying it.
182
+ - The committed badge in `.github/badges/` is produced by the same code path the action runs, and it
183
+ records the protocol digest it was minted under. A re-freeze makes it stale and the test that reads it
184
+ goes red rather than leaving a green pill describing a superseded protocol.
185
+
186
+ It is minted in the **corpus-absent** lane, because that is the lane a clone and the runner see: the
187
+ corpus is not in the repository, so a stranger reproduces `re-derived 612 /612 values`, and the badge
188
+ carries that number and `corpus absent` in its tooltip and in `autorate-badge.json`. On a machine that
189
+ also holds the corpus the same command prints `re-derived 649 /649 values` and writes a badge saying
190
+ `corpus present`; both are the tool's own labels, and the badge never carries one lane's number under
191
+ the other lane's name. Re-mint the committed one from an export:
192
+
193
+ ```bash
194
+ exp=$(mktemp -d) && git archive HEAD | tar -x -C "$exp"
195
+ (cd "$exp" && uv run --frozen python3 tools/action_report.py --path protocol.json --badge-dir badges)
196
+ cp "$exp"/badges/autorate-badge.svg "$exp"/badges/autorate-badge.json .github/badges/
197
+ ```
198
+
199
+ ## Reproduction
200
+
201
+ `docs/RESULTS.md` is generated, never typed: `autorate report` emits it and `autorate
202
+ verify` re-derives every value in it and prints `re-derived N /N values`. It carries no
203
+ timestamp, so a re-run is byte-comparable, and it quotes no path or machine name, so a
204
+ machine without the corpus reproduces the same bytes -- which is what lets CI run the
205
+ gate at all.
206
+
207
+ `docs/REPORT.md` is the same kind of file for a reader who wants one document instead of
208
+ seven: `autorate report --full` emits it, `verify` re-derives it byte for byte under the same
209
+ rule, and every figure in it is read out of an artefact rather than typed into a sentence --
210
+ the certificate shape from `certificate.py`, the five gates from `protocol.json`'s `claims`
211
+ array, each refusal from the artefact that refused, the related work from the rows of
212
+ `docs/VERIFICATION.md` that are `verified`. The flag is additive on purpose: `autorate report`
213
+ with no flag still writes `docs/RESULTS.md`, byte for byte the file this gate has always
214
+ compared.
215
+
216
+ That fallback has a stated limit, and `verify` prints it: with the corpus absent, the
217
+ upstream `SOURCE.json` digests and the inventory snapshot are transcribed from
218
+ `data/inventory.md.json`, not recomputed. The snapshot is policed from the environment
219
+ that can see the corpus, where a stale key goes red.
220
+
221
+ Those two environments are not two speeds of the same gate, and the denominators `verify`
222
+ prints say so: `re-derived 612 /612 values` where the corpus is absent, `re-derived 649 /649
223
+ values` where it is present. Both exit 0, both are green, and they are not the same claim.
224
+
225
+ The corpus is not a committed file. It is the sibling `laya*/research/benchmarks/feishu_zh`
226
+ checkout, which lives outside this repository, so a runner that checks out this repository
227
+ alone can never reach the 649: every CI run is a 612 run, on both of its arms. Green CI is
228
+ therefore evidence about the transcribed lane and nothing else.
229
+
230
+ A 612 run proves a lot inside that lane. Every published number that is a function of a
231
+ committed input re-derives there -- all seven published certificates, each from the committed
232
+ records it was actually made from, G4's slice run from the four committed MASSIVE record files
233
+ it names inside itself, the whole
234
+ of `docs/RESULTS.md` byte for byte, `data/inventory.md` byte for byte, `docs/AUDIT.md` byte
235
+ for byte, `docs/SEVERITY.md` byte for byte, `docs/RESPLIT.md` byte for byte, `docs/SLICES.md` byte
236
+ for byte, `docs/CONTAMINATION.md` byte for byte, `docs/REPORT.md` byte for byte, and the MASSIVE rows
237
+ from their committed pin. What a 612 run
238
+ cannot prove is what its own disclosure says in so many words --
239
+ `this run cannot see whether that snapshot went stale`. The gap is exactly 37
240
+ checks: the 9 upstream `SOURCE.json` digests, the 26 snapshot keys a corpus-present run
241
+ recomputes instead of reading back. On a machine without the archive those 35 do not run at
242
+ all; the figures they would test exist there only as the transcription, so a snapshot that
243
+ had drifted from the corpus would agree with itself and report 612 /612.
244
+
245
+ So the corpus-present run is not a slower, fussier version of the CI job; it is the only one
246
+ in which the transcription has an owner, and it is a local obligation, run before a push,
247
+ because no runner can run it for you.
248
+
249
+ ## The five criteria, and what this tree can show for each
250
+
251
+ v1 is defined by five criteria, G1–G5. `autorate verify` prints one line per criterion with a status that
252
+ can say no -- `not_certifiable`, `measured_not_bounded`, `not_measured` -- read from `protocol.json`, the
253
+ frozen protocol document, which is emitted by a command and never typed. The statuses below are what that
254
+ command prints at this revision; the document is their owner, so this section is a reading of it and not a
255
+ second place to keep them.
256
+
257
+ - **G1, at least three decision tasks certified at `autorate(α = 0.05)` with a 200-resplit empirical
258
+ violation rate: `not_certifiable`.** Six decision tasks have been scored and their records committed;
259
+ zero of the six yield a certificate, and the refusals are published as artefacts rather than rounded
260
+ away. The binding quantity is the evaluation half's case count: the exact one-sided bound on a small
261
+ number of automated cases sits above a 5% budget no matter how few are wrong. The criterion's second
262
+ column is now measured and published rather than promised: `tools/resplit_experiment.py` re-splits every
263
+ scored task 200 times at the rubric this freeze owns, and `data/resplit_200.json` with `docs/RESPLIT.md`
264
+ carries what came out. All six published tasks certify in **0 of 200** splits, so their empirical violation
265
+ rate is recorded as undefined with a zero denominator instead of being imputed as `0.000`; the one row that
266
+ does produce a rate is the synthetic control (200 of 200 splits certify, rate 0.000 over 200 certificates),
267
+ which tests the machinery and is labelled as nothing else. `verify` compares the artefact's own arithmetic —
268
+ certificates plus refusals per task, the rate against its denominator, the rows against the splits — and the
269
+ rubric it recorded against the freeze's, in both lanes. The row is still not close to met: the second column
270
+ exists, the first is zero. What replaces the missing certificate is the ceiling, published rather than
271
+ asserted: `tools/g1_feasibility.py` computes, for each pool size this checkout can actually build, how wide
272
+ the exact one-sided bound is and what error rate would therefore be certifiable at `α = 0.05`, and
273
+ `verify` recomputes every figure in both lanes from `src/autorate/tails.py` and the published certificates.
274
+ So `not_certifiable` is now a measured claim about n and bound width, not an absence of an answer -- which
275
+ is the outcome spec §14 names as a result ("bound width is a first-class output; 'the bound is useless
276
+ here' *is* a certification result"). §14's kill criterion for this row is not evaluable rather than
277
+ fired: its condition compares a worst-group certified `autorate` with 0.30, and with nothing certified there
278
+ is no certified `autorate` to put in that comparison -- the same refusal that keeps the violation rate
279
+ undefined. Both halves of the pivot are published now: the audit multiple G2 measures, and the
280
+ contamination gap section 9 defines, frozen as `data/contamination_probe.json` with its interval and its
281
+ `claim = False` (see the G4 bullet). So the headline this project ships is the audit multiple G2
282
+ measures and the worst-slice refusal G4 names. Read the G1 row as unmet; that is what it says.
283
+ - **G2, the reference README's gate demonstrably violating its implied error rate: `measured_not_bounded`.**
284
+ `autorate audit` measures the gate upstream instructs and `docs/AUDIT.md` publishes the multiplier, but the
285
+ sample cannot bound the claim: on the 2 cases one backend automates, one is wrong, and the exact one-sided
286
+ bound is 0.975. The point estimate misses upward; what the corpus says is that it cannot carry the claim.
287
+ - **G3, at least one ordinal guarantee multiclass conformal prediction cannot express: `not_measured`.** The
288
+ one-sided severity certificate (`P(Y > â(X)) ≤ α`, three canonical bounds, its own refusal reasons) is a
289
+ command, an artefact and a gate row now: `autorate severity` writes `data/cert_severity_ordinal.json` from
290
+ the committed ordinal population, `docs/SEVERITY.md` renders it from those bytes, and `verify` re-derives
291
+ every published quantity of it in both lanes. The status does not move, because the criterion is a contrast
292
+ and not a certificate. A first measured contrast is committed (`tests/test_g3_contrast.py`): on the published
293
+ ordinal population, at the rubric this freeze owns, this repository's ordinal set gate refuses --
294
+ `empty_feasible_set`, publishing no `qhat`, no automation rate and no error bound of any kind -- while the
295
+ severity gate certifies the one-sided bound, reports its measured held-out violation rate and states the
296
+ rule's price; the set gate refuses on all four population variants that file walks. That is silence in a
297
+ named direction, and it is not the criterion -- because the criterion's own wording lost. On the corner of
298
+ the generator where the set gate does certify (`shift = 0`, `error_rate = 0`; 4 of the 64 knob cells
299
+ searched), its interval's top endpoint sits above the gold level in none of the 600 evaluated records
300
+ (200 cases), so
301
+ `P(Y > hi(X)) = 0` lies inside that certificate's own marginal bound of 0.014867 -- computed by
302
+ `tests/test_g3_contrast.py`'s builder in memory and written to no artefact, which is why the digit names its
303
+ producer instead of calling itself published: a coverage claim
304
+ over ordered intervals *does* yield a one-sided bound, and "multiclass conformal prediction cannot express
305
+ one" is false for that construction. The difference that survives is narrower, and it is measured rather
306
+ than asserted: taking that rule costs reporting above the model's own modal level on a large share of cases
307
+ and leaves 36 evaluated records with an empty certified set and so with no top endpoint at all, while the set
308
+ certificate publishes no price, no override rate and no named rule -- the severity artefact certifies a rule
309
+ and states both quantities beside its bound, at the same `delta`. The status stays `not_measured` because the
310
+ criterion is the wider sentence, and the narrowing lives in the frozen document that carries it
311
+ (`protocol.json`'s G3 note, at this digest) rather than in a re-worded criterion: retiring the wider wording
312
+ is a decision with a new freeze, and this section reads its statuses off that document instead of keeping a
313
+ second copy of them.
314
+ - **G4, worst-slice certification refusing rather than averaging: `not_met`.** The behaviour is published now
315
+ rather than printed and discarded: `tools/language_slices.py` certifies the four committed MASSIVE record
316
+ files (11,896 cases) as slices of `slices.language` -- the key the producer declares, not a key invented for
317
+ the run -- and `verify` recomputes that run from the same bytes, 92 field comparisons plus 5 rubric
318
+ rows, in
319
+ both lanes. What it publishes is a refusal: 4 slices, 0 certified, each slice refused at
320
+ the corrected budget (alpha and delta 0.05 / 4 = 0.0125, joint level 0.85), and `n_min_violations` empty --
321
+ so this is a bound failure, not the size floor. The pooled marginal certificate over the same records is
322
+ published beside the slices and refuses too, which is the "rather than averaging" half: nothing in the
323
+ artefact lets the pooled number stand in for the worst slice. Two things keep the row short of met: it is
324
+ per-language only, because `option_count` is 20 for every committed MASSIVE row -- Khmer included, measured
325
+ across all 11,896 rows -- so the (language x option-count) cross the criterion names would be the same four
326
+ slices; and the ranking the criterion's word `worst` asks for cannot be published from a run where
327
+ nothing certifies: `run.worst_claim.worst.group` is `de`, but that is a tie-break, not a measurement -- its
328
+ `run.worst_claim.metric` is `autorate`, and `autorate` is null in all four rows, so the artefact names a
329
+ slice without demonstrating that it is the worst one. The slice the criterion names by example, Khmer, is no longer a gap:
330
+ `data/records_massive_km.jsonl` commits 2,974 per-case rows and this tree measures it, refusing like the
331
+ other three at the same rubric. Upstream's `accuracy 0.000 at mean confidence 0.952` remains upstream's own
332
+ 100-case read, which these 1,487 evaluated Khmer cases neither replicate nor contradict. What the run does
333
+ say is the criterion's own word:
334
+ every refused slice's summary carries `NOT_CERTIFIABLE: reason=empty_feasible_set`, and the command over
335
+ these records exits 3, `EXIT_NOT_CERTIFIABLE`. The criterion's second named output is published beside
336
+ that run: `tools/contamination_probe.py` pools `data/pin_app_benchmark.json` -- upstream's own per-suite
337
+ `in_training` declarations, 27 usable rows and 3 excluded by name because their rounded rate is not a whole
338
+ number of cases -- and publishes `delta_hat` = -0.235208 over [-0.262646, -0.207502], so the in-training
339
+ stratum errs less and the interval is clear of zero. `verify` re-pools it (29 quantities plus 4 budget
340
+ values in both lanes, and re-digests the upstream file where the corpus is visible), and the artefact's
341
+ `claim` is false: the strata are different suites, so task difficulty is confounded with the flag and a gap
342
+ between aggregate rates is a diagnostic, not a proof of leakage. The four marginal certificates, read from their own `detail`, are the at-a-glance version of
343
+ the same evidence: `de` automates 0 of 1,487 cases (no singleton at that coverage, so nothing to bound);
344
+ `en` automates 709 with 43 wrong, upper bound 0.078; `km` 11 with 5 wrong, 0.729; `sw` 39 with 13 wrong,
345
+ 0.477. Every non-empty slice is above the 0.050 budget, and none of the four certifies.
346
+ - **G5, reproducible from a commit with no GPU, no network, no model: `certified`, and the proof is a
347
+ committed transcript.**
348
+ `docs/G5.md` is the record: `tools/g5_export.sh` unpacks a `git archive` of commit `9e6363a` into a scratch
349
+ directory with its own HOME and runs `autorate verify --protocol protocol.json` there --
350
+ `249/249` values with the corpus absent at rc=0 and `284/284` with
351
+ it present at rc=0, and four planted defects that each come back inside the documented
352
+ exit-code contract (rc=2, 4, 4, 4) and are restored byte-identically with the restore
353
+ checked. `tests/test_g5_transcript.py` refuses the file if a recorded code escapes the contract, if a
354
+ lane's two halves disagree, if the digest printed is not the digest of the commit the transcript names,
355
+ if the sentence counting the plants disagrees with the number of plant blocks, or if a single line still
356
+ names a machine path. Both CI jobs run the `--protocol` form on every push, so each tip gets its own
357
+ platform witness of the frozen comparison; `docs/G5.md` states that workflow fact without quoting a run
358
+ id, because a run belongs to the push that produced it and the transcript names a commit instead.
359
+ Three limits stay stated: the archive lane is a local obligation, the
360
+ no-network claim covers the gate steps and not the dependency install, and the drift and contamination
361
+ figures are unpublished, so they are absent from both denominators instead of re-derived.
362
+
363
+ The honest summary is that the gate's reproduction story is the strongest part of this tree today. G4's
364
+ slice criterion now has published evidence and is short of its own wording, not short of an artefact; the
365
+ ordinal criterion is the one whose engine, evidence and wording disagree in a measured way; and G1 remains
366
+ the criterion this corpus cannot meet at all.
367
+
368
+ ## What is here, and what is not
369
+
370
+ The first paired certificate pair is published in `docs/RESULTS.md`: on the archived
371
+ Chinese workplace corpus **both backends refuse** at alpha = beta = 0.05, for different
372
+ reasons. One automates 2 of its 32 evaluation cases and one of those is wrong, so its case
373
+ bound is 0.975. The other automates all 32 with one wrong, and the exact case bound is 0.140
374
+ -- above a 0.05 budget. Even had that one request been right, a zero-error run over n=32
375
+ would have bounded at 0.089, so the corpus cannot support a 5% claim at any error count.
376
+
377
+ That is the result, not a shortfall: the corpus holds 64 independent cases, and the
378
+ project's own feasibility table -- computed as a scratch probe before any certificate was written, and now a
379
+ published artefact, `tools/g1_feasibility.py` writing `data/g1_feasibility.json` and `docs/FEASIBILITY.md`
380
+ with every figure recomputed by `verify` in both lanes -- said a 5% claim was out of reach here.
381
+ A tool that certified anyway would have been selling the exact thing this one
382
+ refuses to sell. Both evaluation halves cover the same 32 cases, so the pair is compared
383
+ case by case on identical inputs.
384
+
385
+ Covered by code and by tests: the `choice` primitive, marginal two-sided control, APS and LAC set sizes,
386
+ Clopper-Pearson one-sided bounds, and the gate that turns a certificate into
387
+ `auto` / `escalate` / `abstain`.
388
+
389
+ Written and tested, but not yet published as evidence: the threshold certifier (`certify_threshold.py`),
390
+ drift-adaptive conformal (`drift.py`), and the contamination stratification (`contamination.py`). Each of
391
+ them is a library with unit tests and no committed artefact that `verify` re-derives, which is the
392
+ difference between "implemented" and "certified" in this project -- see the criteria above. The
393
+ group-conditional (Mondrian) surface and its simultaneous per-slice correction
394
+ (`certify_choice.certify_groups`, `correction.py`) left that list when `tools/language_slices.py` published
395
+ `data/slices_language.json` and `docs/SLICES.md`: those are the artefacts the G4 row above is about, and
396
+ `verify` recomputes the run from the committed records field by field.
397
+
398
+ Published, re-derived, and still not the evidence its criterion asks for: the ordinal one-sided severity
399
+ certificate (`severity.py`), reachable as `autorate severity`, committed as `data/cert_severity_ordinal.json`
400
+ over `data/records_ordinal_synthetic.jsonl`, rendered into `docs/SEVERITY.md` and re-derived by `verify` in
401
+ both lanes. `claim G3` prints `not_measured` beside an artefact that exists, because the criterion is the
402
+ wider sentence -- "multiclass conformal prediction *cannot express* a one-sided ordinal guarantee" -- and the
403
+ control that would establish the contrast has now been run and did not go that way: at the corner of the
404
+ generator where the set gate certifies at all, its interval already yields the bound. What survives is
405
+ narrower and measured, and the narrowing lives in the frozen G3 note rather than in a re-worded criterion. Its
406
+ population is synthetic ordinal data with known answers: no committed records file in this checkout carries a
407
+ `qtype="score"` record, and the artefact's own envelope says so, so nothing here is a measurement of a shipped
408
+ model's ordinal risk.
409
+
410
+ Not implemented at all: per-ask `noul` budgets. The `noul` question type is read, counted and quarantined
411
+ by the same loaders as the others, and there is no certifier for it.
412
+
413
+ `docs/superpowers/specs/` states the design; `docs/superpowers/plans/` states the order.
414
+
415
+ Read `docs/ERRATA.md` before quoting any number from this project's history, and
416
+ `docs/VERIFICATION.md` for which external claims were checked against sources and which
417
+ were withdrawn.