-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfile-budget.json
More file actions
237 lines (237 loc) · 32.5 KB
/
Copy pathfile-budget.json
File metadata and controls
237 lines (237 loc) · 32.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
{
"files": {
"src/bonfire/dispatch/security_hooks.py": 1329,
"src/bonfire/dispatch/security_patterns.py": 521,
"src/bonfire/engine/pipeline.py": 994,
"src/bonfire/handlers/merge_preflight.py": 687,
"src/bonfire/handlers/sage_correction_bounce.py": 904,
"src/bonfire/onboard/config_generator.py": 560,
"tests/integration/test_budget_enforcement.py": 508,
"tests/unit/test_architect_handler.py": 630,
"tests/unit/test_bard_handler.py": 1345,
"tests/unit/test_bon_1072_cost_accounting_close.py": 862,
"tests/unit/test_bon_1074_onboard_polish.py": 634,
"tests/unit/test_bon_1075_read_symmetric_hardening.py": 759,
"tests/unit/test_classifier.py": 587,
"tests/unit/test_cost_analyzer.py": 894,
"tests/unit/test_dispatch_runner.py": 1184,
"tests/unit/test_engine_checkpoint.py": 800,
"tests/unit/test_engine_gates.py": 621,
"tests/unit/test_engine_pipeline.py": 1895,
"tests/unit/test_engine_pipeline_tool_policy.py": 664,
"tests/unit/test_envelope.py": 591,
"tests/unit/test_event_bus.py": 1081,
"tests/unit/test_event_consumers.py": 967,
"tests/unit/test_events.py": 675,
"tests/unit/test_git.py": 754,
"tests/unit/test_github.py": 846,
"tests/unit/test_init_gitignore_width.py": {
"purpose": "the two-direction width contract for the .gitignore that bonfire init seeds: the operator-local paths under .bonfire/ are covered in BOTH on-disk shapes the knowledge store's single default path can take (a LanceDB directory and a SQLite regular file), while every committable sub-path (sessions, context.json, the cost ledger) stays stageable. Split out of test_tools_section_is_local.py, which owns the tools-local-file pins and was at its frozen size: this is one contract with its own module, and the split returns that file BELOW its frozen number rather than raising it. Graded by real git check-ignore against a real repository with git's own global and system config neutralised, so a contributor's excludes file cannot make it pass or fail."
},
"tests/unit/test_merge_preflight_handler.py": 1139,
"tests/unit/test_onboard_scanner_claude_memory.py": 562,
"tests/unit/test_onboard_scanner_cli_toolchain.py": 504,
"tests/unit/test_onboard_scanner_mcp_servers.py": 863,
"tests/unit/test_onboard_scanner_vault_seed.py": 532,
"tests/unit/test_onboard_server.py": 996,
"tests/unit/test_persona_cli_hardening.py": {
"purpose": "persona set escaped-quote rewrite + slug-validation parity contract"
},
"tests/unit/test_persona_loader.py": 526,
"tests/unit/test_plan.py": 655,
"tests/unit/test_prompt_compiler.py": 1256,
"tests/unit/test_protocols.py": 892,
"tests/unit/test_safe_write.py": 617,
"tests/unit/test_sage_correction_handler.py": 1294,
"tests/unit/test_sage_correction_resolved_gate.py": 550,
"tests/unit/test_security_hooks_canonicalizer_adversarial.py": 605,
"tests/unit/test_security_hooks_cross_platform.py": 959,
"tests/unit/test_security_hooks_failsafe.py": 554,
"tests/unit/test_steward_handler.py": 570,
"tests/unit/test_tool_policy.py": 666,
"tests/unit/test_tools_section_is_local.py": 893,
"tests/unit/test_wave_11_halt_branch_completeness.py": 769,
"tests/unit/test_wave_11_security_cleanup.py": 634,
"tests/unit/test_wizard_handler.py": 1343,
"tests/unit/test_workflow.py": 897,
"tests/unit/test_ws_token_oob_handoff.py": 673,
"tests/unit/test_xp_consumer.py": 587,
"tests/unit/test_session_store.py": {
"purpose": "new coverage for the shared session-read store layer"
},
"tests/unit/test_session_handoff_doc.py": {
"purpose": "new coverage for the handoff-document renderer"
},
"tests/unit/test_session_lifecycle_cli.py": {
"purpose": "new e2e coverage for the status/resume/handoff verbs"
},
"tests/unit/test_run_command.py": {
"purpose": "unit coverage for the run verb (prompt to plan to engine driver)"
},
"tests/unit/test_sqlite_vault.py": {
"purpose": "contract + persistence coverage for the sqlite vault backend"
},
"tests/e2e/scripts/e2e-runner.sh": {
"purpose": "release-gate box runner: one linear phase sequence (install the wheel under test, prove its provenance and its dependency floors, execute bonfire run, observe, grade, arbitrate the verdict). Over the 500-line guideline and shrink-only: the phases share the trap-guaranteed verdict emitter and the failure-reason vocabulary, so splitting them across files would duplicate that state rather than reduce it, and roughly half the file is the comment surface a reviewer needs to answer which artifact was installed and which command was run.",
"baseline": 779,
"measured": 968,
"note": "THE BASELINE IS NOT MOVED BY THIS WORK, and the file is over it. 779 already understated the file before this defect pass touched it (it measured 843 at the parent commit); the balance to 968 is this pass's own growth \u2014 the container-side WAVE shape check, the gate-verdict quarantine, and carrying the runner's failure reasons into a verdict the fixture gate already wrote. Recorded as declared debt rather than as a re-baseline: raising a shrink-only number by the very change it constrains is a ratchet loosening, and only a maintainer may ratify one. Bringing this file back under 779 is a standalone refactor, not a rider on a defect pass \u2014 and it must not be paid for by deleting the comment surface this entry names as the reason for the size. NOTE: the file-budget gate walks .py files only, so this entry is a documented, self-imposed budget rather than a gate-enforced one."
},
"tests/e2e/scripts/e2e-box.sh": {
"purpose": "release-gate box host driver: auth detection, wheel build + provenance manifest, image build with an explicit cache policy, container launch, verdict readout. Shrink-only, and under the 500-line SOLID guideline.",
"baseline": 357,
"measured": 373,
"note": "THE BASELINE IS NOT MOVED BY THIS WORK, and the file is 16 lines over it. Those lines are the <wave> usage guard \u2014 a missing, empty or non-integer argument now exits 7 rather than taking bash's ${1:?...} status of 1, which is the code reserved for a FAIL verdict \u2014 plus its rationale comment. Declared as debt for a maintainer to rule on rather than folded into a raised number. NOTE: the file-budget gate walks .py files only, so this entry is a documented, self-imposed budget rather than a gate-enforced one."
},
"src/bonfire/engine/composition.py": {
"purpose": "the composition root: the one place the real handlers, gates, tool policy and project root are chosen, extracted from the CLI's _default_engine so that it has somewhere to be tested from"
},
"tests/integration/conftest.py": {
"purpose": "shared fixtures for the two composition-root modules: a throwaway git work tree and a transport that records the ClaudeAgentOptions the backend built"
},
"tests/integration/test_composition_root.py": {
"purpose": "calls build_default_engine directly rather than through the injected-factory seam that hid the un-wired engine; covers the four omitted constructor arguments, registry integrity, and the unknown-gate refusal"
},
"tests/integration/test_project_root_trust.py": {
"purpose": "hostile-CLAUDE.md fixture proving a cloned repository's project settings are no longer ingested, with the counterfactual and both documented opt-ins so the assertion cannot go vacuous"
},
"tests/integration/test_claude_workflow_author_guard.py": {
"purpose": "structural guard on the agent workflow's author_association allow-list, derived from the workflow's own on: block so a trigger added without a guard fails here"
},
"tests/integration/test_config_round_trip.py": {
"purpose": "writes bonfire.toml with the real generator and loads it with the real BonfireSettings, which the per-fragment emitter tests structurally cannot: they parse each builder's output in isolation, so a section that was valid TOML and unloadable passed them. Also pins that persona set refuses rather than corrupting, and that persona list never reports an active persona off a config it could not read"
},
"tests/unit/test_version_truth.py": {
"purpose": "pins pyproject's version, what bonfire --version prints, the __version__ import fallback and the newest CHANGELOG heading to one value, restoring on every run the guard that release.yml lost when the tree was reconciled to the published product line"
},
"src/bonfire/engine/gate_state.py": {
"purpose": "where the suite-backed gates get their state: the SuiteProbe seam, the pytest subprocess probe, and the error a gate raises when it cannot observe"
},
"src/bonfire/engine/suite_gates.py": {
"purpose": "the three gates that grade a live pytest observation instead of the agent's narration"
},
"tests/integration/test_gates_grade_state.py": {
"purpose": "control rods for the state-grading gates: a real repo, a real pytest run, and the old prose predicates written out so the rods measure a difference"
},
"tests/unit/test_engine_gates_state.py": {
"purpose": "the gates-grade-state contract in both directions plus the unevaluatable case"
},
"tests/unit/test_verify_suite.py": {
"purpose": "contract for the pytest-run parser the suite-backed gates grade"
},
"src/bonfire/dispatch/artifacts.py": {
"purpose": "the producer Envelope.artifacts never had: maps the agent's file-mutating tool calls onto artifact records, at the only layer that sees which files were touched"
},
"src/bonfire/handlers/sage_dispatch_options.py": {
"purpose": "the Sage dispatch options, split out of the handler because what they have to get right is a contract with the SDK backend rather than handler logic; the standalone dataclass they replace carried five fields where the backend reads eight"
},
"tests/unit/test_dispatch_artifact_capture.py": {
"purpose": "pins the tool-use to artifact mapping, including the cross-module half a same-module assertion cannot reach: that the types emitted are the ones the publisher filters on"
},
"tests/unit/test_engine_artifact_carry.py": {
"purpose": "pins that artifacts recorded by one stage reach the stage that publishes them; the engine builds a fresh envelope per stage, so a producer alone left the publisher empty"
},
"tests/unit/test_sage_dispatch_options_contract.py": {
"purpose": "drives the real backend body with the Sage options so an attribute the backend starts reading fails here rather than escalating silently, and pins the cwd trust boundary"
},
"tests/unit/test_publish_path_gate_contract.py": {
"purpose": "pins the publishing path against the release gate's branch pattern with a real git repo and a real bare remote: the created ref, the pushed ref and the PR head must agree"
},
"tests/integration/test_publisher_receives_artifacts.py": {
"purpose": "drives the real composition root with a transport that reports an Edit, closing the half test_composition_root cannot: that a reported write reaches the publisher"
},
"tests/integration/test_run_artifacts.py": {
"purpose": "drives the real build_default_engine and asserts the two run artifacts the release gate reads actually land: the cost ledger and the review verdict. Parametrised over three reviewer replies so a producer that hardcoded its verdict would fail rather than pass"
}
},
"packages": {
"src/bonfire/dispatch": 2869,
"src/bonfire/engine": 1981,
"src/bonfire/handlers": 3280,
"src/bonfire/onboard": 4460,
"tests/integration": 1702,
"tests/unit": 74023
},
"package_raises": [
{
"package": "src/bonfire/dispatch",
"from": 2865,
"to": 2869,
"lines": 4,
"reason": "Envelope.artifacts had one reader and no producer, so the publishing stage of standard_build refused on every run. The producer has to live at the dispatch layer because that is the only layer that sees the agent's tool-use blocks: a handler is given result text and nothing else, and diffing the working tree cannot separate the agent's writes from anything else dirty. Wiring it into sdk_backend.py costs exactly four lines and they are irreducible: the import, the content_blocks accumulator, the append inside the block loop, and the artifacts= entry in the model_copy. Every other package this change touched was brought back under its ceiling by structure rather than by number, and three baselines were ratcheted down. This one could not be, so the number moved and the architecture did not. Explicitly rejected: excusing sdk_backend.py with a purpose entry, which would drop 306 lines out of the ratchet to accommodate 4.",
"approver": "Anta, 2026-07-27, via the lane conductor on PR #247"
},
{
"package": "src/bonfire/handlers",
"from": 3194,
"to": 3250,
"lines": 56,
"reason": "The reviewer's verdict had a reader and no writer: the release gate and docs/box-operator.md both grade .bonfire/review-verdict.json, and nothing in src/bonfire ever produced it -- the verdict lived on the returned envelope's metadata and died with the process. The producer has to live in wizard.py because that is the only place the verdict exists as a decision rather than as text: the parser distinguishes an agent-emitted verdict from the fail-safe one it substitutes, and an artifact that lost that distinction would report a decision nobody made. The 56 lines are the import trio, the path constant, a ten-line write helper, the project_root collaborator, and the call site with its document and its failed-write branch. Explicitly rejected: giving wizard.py a purpose entry, which would drop 443 lines out of the ratchet to accommodate 56 -- the same trade the dispatch raise below rejected. Also rejected: writing the artifact from an event consumer in the unbudgeted events package, which would have cost zero package budget and needed a new event type carrying the verdict, so the artifact would appear only when the stage completed -- and the box's reviewer stage cannot complete, because it has no gh and post_review raises there. Budget is not a reason to move a producer off the path that produces. NOTE: wizard.py now measures 499 against the 500-line new-file cap, so the next change to it has to split the module rather than compress it.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "tests/unit",
"from": 73199,
"to": 73410,
"lines": 211,
"reason": "tests/unit measured EXACTLY 73199 on bare origin/main -- the ceiling and the measurement were the same number, so the package had zero headroom and no pull request could add a unit test at all. What the 211 lines buy: tests/unit/test_init_first_run_refusals.py, eight tests over the first command a stranger runs. bonfire init met four hostile-but-ordinary path shapes with a raw Python traceback or, in one case, with a success claim -- a directory named bonfire.toml satisfied Path.exists(), the write was skipped, and the success block printed 'Already present: bonfire.toml (project config)' with exit code 0 over a project no Bonfire command can read. Seven of the eight tests fail on origin/main's behaviour with the fix removed, each for its own stated reason (PermissionError from the first write, PermissionError from the gitignore append, exit 0 with the success banner, IsADirectoryError from inside the safe-read helper, and FileExistsError three times out of mkdir(exist_ok=True) for a regular file, a second regular file, and a dangling symlink). The eighth passes on both sides on purpose: it is the control rod against a guard that learns to refuse everything, and without it the other seven would be satisfied by a command that refuses unconditionally. Four distinct defects, seven distinct failure modes, one negative control -- that is the smallest honest count, not a padded one. Explicitly rejected, in order of how tempting each was: (1) shipping fewer tests to fit -- the control rods ARE the deliverable, and four defects verified by two tests is a weaker claim than the one the pull request makes; (2) parking the file in an unbudgeted tree (tests/smoke, tests/dispatch), which costs zero budget and is dodging the ratchet rather than restructuring, and buries a regression contract where nobody maintaining init would look for it; (3) tests/integration, which is frozen at 1363 with zero headroom of its own and is the wrong home anyway for single-command CLI tests; (4) shrinking another test file to make room, which would have raced two other lanes live in this tree for budget and is worse than asking; (5) a purpose entry for the new file, which is an exemption in everything but name and would drop the file out of the ratchet permanently to accommodate one change -- the same trade the dispatch and handlers raises below both rejected, and exemptions.json is at 52/52 besides. NOTE for the next reader: a ratchet that reaches its own measurement has stopped preventing bloat and started taxing test coverage. Every future unit test in this repo now needs a raise. That is a design question about the tests/unit ceiling, not something a lane can settle.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "src/bonfire/handlers",
"from": 3250,
"to": 3280,
"lines": 30,
"reason": "src/bonfire/handlers measured EXACTLY 3250 on bare origin/main -- the ceiling and the measurement were the same number, so the package had zero headroom and no behaviour-preserving complexity refactor could land in it at all. BardHandler.handle measured 19 against a limit of 10 and was never in complexipy-snapshot.json: drift, not grandfathered debt, and it landed because CI does not run the complexity gate (the pinned quality workflow lists its steps by hand and complexipy is not among them). What the 30 lines buy: three extractions that take handle from 19 to 7, each measured so the branches are removed rather than relocated -- _create_branch 6 (the branch-creation try/except and its 'already exists' probe), _partial_metadata 3 (the salvage map on the throw path), _failed 0 (the FAILED envelope four refusal paths each built by hand). The suite is the control and its counts are identical on both sides: 5746 passed, 3 skipped, 37 xfailed, 20 xpassed. The lines are what an extraction costs and nothing else -- a signature, a docstring, and a call site, three times over. Explicitly rejected, in order of how tempting each was: (1) adding the function to complexipy-snapshot.json, which is the exact move the remediation programme exists to end and would have cost zero budget; (2) the seven-line minimum variant, which reaches handle 9 by compressing the salvage map into a table-and-filter -- it was measured for complexity and for lines and never run against the suite, and an unproven refactor is not cheaper, it is unmeasured; (3) deleting comments or blank lines to buy the lines back, which is gaming, and pointless besides because the gauge measures max(physical, statements) so compression cannot manufacture headroom; (4) splitting the helpers into a new file carrying a purpose entry, which is the tool's own designed escape hatch and would have made the package total FALL rather than rise -- rejected because it drops a live file out of the ratchet permanently to accommodate one change, the same trade the three raises above all rejected; (5) moving the helpers outside src/bonfire/handlers into an unbudgeted package, which costs zero budget and is dodging the ratchet, and puts a publisher-specific envelope builder somewhere no one maintaining the publisher would look; (6) hunting dead code in the package to fund the lines -- five candidates checked, every one live. NOTE for the next reader, and it is the finding that explains why this debt never moves: every handler file holding an un-grandfathered violation also carries a suppression that exemptions.json pinned by LINE NUMBER (bard 282, wizard 492, steward 166, sage_correction_bounce 320/488/539, preflight_pytest 321, architect 72/214). Any refactor of any of those functions shifts a pinned line and turns cf-exemptions red, so the register that records accepted debt structurally forbids the refactoring that would retire it. This lane re-anchored only its own row, to a symbol; the other eight are still line-pinned. wizard.py was left untouched on purpose: at 499 against the 500-line new-file cap, in a package that had zero headroom, with its own suppression pinned at line 492, it is blocked three ways before the first edit.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "src/bonfire/engine/pipeline.py",
"from": 989,
"to": 994,
"lines": 5,
"reason": "The engine had no checkpoint write site, so bonfire status / resume / handoff read an artifact bonfire run never produced. Five lines is what the write site costs pipeline.py after the mechanism was moved out of it twice: the PipelineResult construction and the durability reasoning went to SessionStore.save_progress (session/ is unbudgeted and is the layer that owns what a stored record means), and the sink-is-None check plus the error handling went to a write_progress function in engine/checkpoint.py (the module that owns how a checkpoint is written). What is left in pipeline.py is irreducible: one import, one TYPE_CHECKING import, one constructor keyword, one assignment, one call, and a four-line comment stating why the call sits before the budget check rather than after it -- the group is paid for whether or not the next line halts, so a halt that discarded the record would bill it again on resume. Explicitly rejected: deleting that comment to land on exactly 989, which is buying a number with the reasoning for the single most subtle ordering decision in the change. Also rejected: inlining the call site's guard and try/except into _run_inner, which needs no new lines in pipeline.py at all but adds two branches to a function frozen in the complexity snapshot at 23. Also rejected: giving pipeline.py a purpose entry, which would drop 994 lines out of the ratchet to accommodate 5.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "src/bonfire/engine",
"from": 1903,
"to": 1981,
"lines": 78,
"reason": "The same producer, measured at the package. 5 of the 78 are the write site in pipeline.py; the other 73 are in engine/checkpoint.py, which gains the CheckpointSink Protocol the engine writes through and the write_progress function that drives it. The Protocol has to exist because bonfire.session imports bonfire.engine, so naming SessionStore in the engine's signature would point the dependency back into a cycle; import-linter reports 1 contract kept with the Protocol in place. Both belong in checkpoint.py rather than anywhere cheaper: that module already owns the atomic tmp+replace write and the symlink refusals, and the new code is the rule for when that machinery runs. Explicitly rejected: putting the Protocol in bonfire/protocols.py, which is unbudgeted and would have cost zero -- but CLAUDE.md's release gate names 'the four runtime_checkable extension protocols' as a v0.1 trust-triangle item, and quietly making it five edits a documented count from a lane that does not own that doc. Also rejected: writing the checkpoint from an event consumer in the unbudgeted events package -- StageCompleted carries stage_name, agent_name, duration and cost and no Envelope, so a consumer cannot populate CheckpointData.completed at all. That one is not a budget trade; it does not work.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "tests/integration",
"from": 1363,
"to": 1702,
"lines": 339,
"reason": "tests/integration measured EXACTLY its ceiling on origin/main (1363/1363), so the package had zero headroom and no pull request could add an integration test at all. What the 339 lines buy: tests/integration/test_run_checkpoints.py, eight tests that run the engine returned by build_default_engine. They have to live in tests/integration and they have to go through the composition root: this defect family -- Envelope.artifacts, review-verdict.json, costs.jsonl and now the checkpoint -- is invisible to unit tests by construction, because every unit test of the run path injects its own engine factory, so the wiring nothing exercises is the wiring nothing can catch. A test that assembles the object graph by hand re-implements the wiring under test and passes whether or not the product is wired. Both halves are rodded: removing the engine's write site turns 5 of the 8 red, and removing checkpoint_sink= from build_default_engine turns the same 4 red plus the wiring assertion, while the two tests that must not depend on the write stay green. Explicitly rejected: putting these in tests/unit, which would have been the same line count against a ceiling a second live lane needs this round -- and would have meant hand-building the engine, which is the measurement error this file exists to avoid. Also rejected: a purpose entry for the new file, which registers it out of the ratchet entirely rather than paying for it.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "tests/unit",
"from": 73410,
"to": 73777,
"lines": 367,
"reason": "tests/unit measured 73409 against a ceiling of 73410 -- one line of headroom, the second consecutive lane to arrive at a ratchet sitting on its own measurement (the raise immediately below moved it 73199 -> 73410 and landed it right back on the number). What the 367 lines buy: tests/unit/test_failure_is_not_a_completion.py, twenty tests over four places where a failure recorded itself as a completion or gave a reason that was not the reason that occurred. The money-facing one is the cost ledger: a halted run and a run that finished one stage wrote rows differing ONLY in timestamp, so 'bonfire cost' -- the operator's record of what they were charged and why -- could report the spend but not whether it bought a finished run or a crash. The others are the XP penalty's reason, a crashed scanner reported to the browser as a clean scan of nothing, and every client-frame validation error reported as message_too_long. Each defect gets a regression test that fails on origin/main's behaviour AND a negative control asserting a genuine success still records as a success, so none of it can be satisfied by code that reports everything as broken; two further tests pin the on-disk migration (pre-field rows still load, and still aggregate through CostAnalyzer, without being fabricated into successes). Explicitly rejected: (1) shipping fewer tests -- the control rods ARE the deliverable and the negative controls are what stop the fix from being 'call everything a failure'; (2) parking the file in an unbudgeted tree, which costs zero budget and is dodging the ratchet rather than restructuring, and was rejected on the same grounds by the raise below; (3) a purpose entry, an exemption in all but name that would drop the file out of the ratchet permanently; (4) shrinking another test file to make room, which races two lanes live in this tree right now; (5) padding the ask to leave headroom for the next lane -- the number is the measurement, and a lane granting itself slack is how a ratchet dies. The file was tightened before the ask was sized (a shared sink helper and a shared legacy-row constant replaced duplicated setup, and two pairs of tests became parametrized), which took the measurement from 389 to 368.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "src/bonfire/onboard",
"from": 4416,
"to": 4460,
"lines": 44,
"reason": "src/bonfire/onboard measured EXACTLY 4416 against a ceiling of 4416 -- zero headroom, verified by a one-line probe file producing 'package src/bonfire/onboard is 4417 lines'. The 44 lines fix two records in this package that reported a failure as a success. In orchestrator.py a scanner that CRASHED emitted ScanComplete(item_count=0), identical to a scanner that ran clean and found nothing, so the browser said 'we scanned and found nothing' over a scan that had died; _run_one now returns (count, failed) and reports the real exception, and run_scan totals failed panels so an all-crashed run does not summarise as total_items=0. In flow.py every ValidationError on a client frame was reported as message_too_long -- including a frame with no text field at all and a frame whose text was a number -- sending the user to shorten a message that was never long; _frame_rejection now keeps message_too_long only for a genuine pydantic string_too_long and names the actual cause otherwise. protocol.py carries the three new fields, all defaulted to the success shape so an older producer's frame still parses. Explicitly rejected: (1) reporting the crash only to the log, which is where it already went and is not visible to the browser that is the actual consumer; (2) compressing docstrings elsewhere in the package to buy the lines back, which trades documentation for budget and measures as nothing anyway since the unit is max(physical, statements); (3) moving the fix out of onboard into an unbudgeted package, which would take the producer off the path that produces. The protocol.py docstrings WERE trimmed once for size before this number was taken.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
},
{
"package": "tests/unit",
"from": 73777,
"to": 74023,
"lines": 246,
"reason": "exemptions.json anchored 44 of its 52 entries by LINE NUMBER, so inserting any line above a registered noqa silently un-registered a blessed exemption and cf-exemptions then reported UNREGISTERED_SUPPRESSION against the suppression -- naming the code as the culprit when the registry pointer was what rotted. That message is convincing: it cost three lanes an afternoon and nearly bought a rewrite of two load-bearing error barriers that a measurement showed would have turned 10 tests red. This PR converts 41 of the 44 to symbol anchors; the 246 lines are tests/unit/test_exemption_anchors_are_drift_proof.py, which stops the class from coming back. It pins three things nothing else pins: (1) every entry is symbol-anchored except an explicit map of the three that provably cannot be, each carrying the reason -- and the map must match the registry EXACTLY, so a re-introduced line anchor fails AND a stale pin fails; (2) every entry resolves to exactly one live suppression, which catches an ambiguous anchor that blanket-blesses a whole function and a dead entry that covers nothing; (3) the small ast+tokenize resolver the first two use agrees entry-for-entry with cf_quality's own _scan_src/_matches whenever the kit is importable, so the mirror cannot drift from the gate it mirrors. A fourth test is a control rod on the measure itself: a synthetic module with two S101 suppressions in one method must resolve to TWO lines, so the assertion is proved able to fail rather than only observed passing. Explicitly rejected: (1) parking the file at tests/ root, which is unbudgeted and would have cost zero -- there is a repo-wide pin already living there so the placement was arguable, but two earlier raises in this same file rejected the unbudgeted tree as dodging the ratchet and the same answer applies here; (2) a purpose entry, which registers the file out of the ratchet permanently; (3) dropping the kit-agreement test to save ~25 lines, which is the only thing keeping the local resolver honest and would leave a gate that grades a copy of the rule instead of the rule; (4) relying solely on the kit's existing EXEMPTION_ENTRY_OVERLOADED violation, which does catch an ambiguous anchor (rodded: it fails with 'entry 47 covers 2 live suppressions') but does NOT catch a re-introduced line anchor or a dead entry, which are the two failure modes this lane was fired over; (5) padding the ask -- 246 is the file's measured line count and nothing else. The file was tightened before the number was taken (a duplicated pin reason hoisted to one constant, the pin map collapsed from exploded tuples to single lines), which took the measurement from 261 to 246.",
"approver": "Anta gates via PR merge -- raise surfaced in the PR body and in the lane report, not taken silently"
}
]
}