Repository navigation
tech-debt(guards): every ratchet baseline is produced by its own detector, so its blind spots become silent permanent exemptions #15897
Description
Every ratchet in this repo freezes a baseline that its own detector produced. At the moment of freezing, that baseline cannot distinguish "the tree contains N instances" from "the detector can see N instances" — and nothing in the pattern ever forces the difference to surface.
The consequence is specific: whatever the detector is blind to becomes permanently exempt, without appearing in the baseline — the list whose entire purpose is to record exemptions. An exemption you can read is a decision. An exemption caused by a blind spot is invisible, and the ratchet reports clean forever.
The worked example, measured
#15896 added a guard forbidding path:LINE citations in comments, with a baseline of the citations that predate the rule. Its scan matched lines starting with #, or — for Python — lines containing a triple quote. That matches the line opening or closing a docstring, never a line inside one, and in this codebase the citations live in docstring bodies.
- baseline as measured by the detector: 8
- true population, re-derived independently: 26
Both numbers were correct about their own question. The 8 was never a count of the tree; it was a count of what the filter could see. Freezing it would have made 18 citations permanently exempt with no record that they existed.
It was caught only because the population was re-derived a second way — git ls-tree enumeration instead of Path.glob, tokenize.STRING tokens instead of AST docstring spans — and the two runs compared. Fixed in #15896; both implementations now agree at 26 with an empty symmetric difference.
The PR's stated justification for its rule was "it costs nothing, because the form is already rare — 8 instances in 464 files." That argument was measuring the instrument.
This is structural, not one bad filter
Verified against the other ratchets on Dev_new_gui:
repo_tests/python_file_size_ratchet_baseline.py — its own docstring at :536 describes the mechanism:
A regeneration that re-measures the tree emits
"path": countand nothing else
The baseline is the detector's output. There is no second derivation.
repo_tests/frontend_api_contract_ratchet_test.py:71-94 — _BASELINES holds literals ("inline_generics": 577, 87) produced by _inline_generic_count at :185. That counter is known defective: #15771 records that it counts matches inside comments. So 577 cannot be read as a count of inline generics — it is 577 detector-hits, an unknown number of which are comments, and no inspection of the number can separate them. The baseline silently encodes the detector's defect and then enforces it.
pipeline-scripts/hardcoded_values_baseline.txt — 1411 entries, and this one is the closest to right: detect-hardcoded-values.sh offers --audit-baseline ("Fail on baseline entries that match nothing") and --prune-baseline.
That last one matters for scoping this issue, because it shows two independent failure directions and only one is guarded anywhere:
| Direction | Question | Guarded today |
|---|---|---|
| staleness | does every baseline entry still match something? | yes, in places — --audit-baseline, and #15896's test_the_baseline_still_describes_real_citations |
| blindness | does the detector's population equal the tree's? | nowhere |
Staleness is checkable with the detector alone, which is why it got built. Blindness is not checkable with the detector alone by definition — that is the whole problem.
Why review does not catch it
Reading the detector re-uses the model that produced the blind spot. In #15896 the author wrote a test asserting the fixed behaviour and it passed against the broken filter, because the module's docstring quotes a triple-quote character while explaining the bug — so the prose describing the blind spot was what satisfied the test for it. It was caught by reverting the filter and re-running, not by reading.
That generalises: once the detector, its baseline, and its test share an author, inspection cannot reach the class. Only a second world to compare against can.
Proposed remedy
Not a permanently maintained second implementation — a cross-check at the only two moments it can pay:
- At freeze. Before a baseline is committed, derive the population a second way — different file enumeration and different matching — and require set equality, not just an equal count. Record the second derivation in the baseline file so a later reader knows it was done and how.
- Whenever the matcher changes. A change to what the detector matches (not what it reports) invalidates the cross-check and needs a fresh one. A change to formatting or messaging does not.
A count-only comparison is not sufficient: two implementations can agree on a total and disagree on membership. #15896's check compared sets and found the symmetric difference empty, which is the assertion worth making.
Acceptance criteria
- Each existing ratchet baseline has its population re-derived by an implementation that shares neither enumeration nor matching with its detector, and the result recorded — file-size,
inline_generics, hardcoded-values, commit-trailer, and docs(guards): fix eight stale referents and guard the two classes behind them (#15877, #15887) #15896's citations - Any baseline whose independent count differs is corrected, and the delta stated in the issue for that ratchet
-
inline_genericsspecifically is re-derived with comments stripped, since bug(ci): the inline_generics ratchet counts comments, so documenting the fix fails the build #15771 establishes its counter is defective — the corrected number replaces 577/87, or bug(ci): the inline_generics ratchet counts comments, so documenting the fix fails the build #15771 is fixed first and the baseline regenerated after - The convention is written down where ratchet authors will meet it, with the two-directions table above and the "compare sets, not counts" rule
- A worked example is linked so the next author sees a real 8-vs-26, not an abstraction
Explicitly out of scope
Building a generic cross-check harness. Each ratchet matches something different and a shared harness would reintroduce the shared implementation this issue is about. The second derivation should be ad hoc by construction — that is the property that makes it independent.
Notes for whoever picks this up
Order by consequence, not by ease: inline_generics first, because it is the one where the detector is already proven defective (#15771) and the baseline is therefore known to encode it. The others are unverified rather than known-wrong.
This does not need a write slot to start — the re-derivations are read-only analysis, and the finding for each ratchet can be posted before any code changes.
Credit: shape identified jointly while reviewing #15896; the --audit-baseline distinction came from reading detect-hardcoded-values.sh and is what keeps this issue from overclaiming that nothing is guarded.
Activity
mrveiss commentedon Sep 6, 2026
Re-derivation 1 of 5 —
inline_generics: 577 → 573, 87 → 86First of the independent re-derivations this issue asks for. Done read-only, no write slot needed, which is the point worth noting for scheduling the rest.
Independence
frontend_api_contract_ratchet_test.py)root.rglob("*")on the working treegit ls-tree -r origin/Dev_new_gui_INLINE_GENERIC_REover raw file textThe lexer walks the source character-wise tracking string literals, so
//inside"https://…"is not mistaken for a comment — a naive strip would have deleted the rest of those lines and undercounted.Control first
My enumeration reproduces the detector's raw counts exactly — 577 and 87. That is what licenses the comparison: it establishes the two runs are looking at the same file set, so the delta below is attributable to matching and not to scope. Without this the corrected numbers would be uninterpretable.
Result
autobot-frontendautobot-slm-frontendAll five verified by hand as JSDoc block-comment content, e.g.
useApiResource.ts:17and:25:Others:
useErrorHandler.ts,useFakeProgress.ts,usePrometheusMetrics.ts:170.I also checked the opposite direction — a call split as
apiClient\n .get<T>(cannot match a regex requiring the dot adjacent to the identifier. Zero instances in either app, so there is no compensating undercount and the deltas are the whole story.What this means for #15771
The over-count is 4 and 1 — real, and smaller than the framing of "an unknown comment fraction" might have suggested. That is worth stating plainly rather than letting the issue imply a large correction.
The actionable part is unchanged, and it is not the size:
Whoever fixes #15771's counter must lower the baseline to 573/86 in the same commit. Carrying 577/87 against a corrected counter silently grants four and one free violations — headroom nobody decided to give, indistinguishable afterwards from work that was never done. That is this issue's failure mode inverted: not a false improvement, but false slack, and equally invisible.
Scope of this claim
This establishes the comment fraction and rules out the multi-line undercount. It does not establish that the remaining 573 are all genuine inline generics —
\b\w*[aA]pi\w*\.matches any identifier containing "api", so a non-API object named e.g.rapidCache.get<T>would count. I did not test that, and it is a separate question from the defect #15771 records.Next: hardcoded-values, file-size, commit-trailer. Taking them in that order — hardcoded-values has
--audit-baselinealready, so its re-derivation also tests whether that guard's existence changed the outcome.mrveiss commentedon Sep 6, 2026
Re-derivation 2 of 5 — hardcoded-values: the congruence is the finding
This is the case where staleness and blindness separate most cleanly, because this detector already guards the first one.
The tautology, measured
The 1363 baseline entries break down by file extension as:
HV_SCAN_EXTENSIONSispy|ts|vue|js|sh|yml|yaml. The baseline's extension set is exactly that list, with zero entries outside it.That congruence is not evidence the tree is clean outside those extensions. It is evidence of nothing at all — the baseline is the detector's output, so it contains what the detector scans and cannot contain anything else. A file type that was never opened is indistinguishable, in the baseline, from a file type that was opened and found clean.
--audit-baselineconfirms the distinction rather than closing itdetect-hardcoded-values.shoffers--audit-baseline("fail on baseline entries that match nothing") and--prune-baseline. Both are real and both work the same direction: does every recorded entry still correspond to something? That is checkable with the detector alone, which is why it exists.The other direction — does the detector's population equal the tree's? — is not checkable with the detector alone, because the instrument's blind spot is inherited by any census it performs. Hence unguarded here, as everywhere.
What blindness costs here, concretely
Filed as #15903.
scripts/lib/hardcoded-value-rules.sh:149definesHV_ACCOUNT_POSITIONmatchingUser=/Group=— systemd unit directive syntax, which occurs in essentially no other file type. Applied verbatim to non-comment lines:.serviceunits and.service.j2templates that are the unitsThe rule catches the scripts that generate systemd units and cannot see the units. Its author wrote that syntax deliberately, so the intent is on the record; the extension list makes it unreachable.
Not a claim that those 32 are violations — a unit naming a service account is plausibly correct. The claim is that nobody decided. A baseline entry is a recorded judgement; an unscanned file is not a judgement of any kind, and the baseline offers no way to tell the two apart.
Also: the header's count has drifted
hardcoded_values_baseline.txt:17— "All 1408 entries below carry exactly three." The file holds 1363. The invariant it asserts still holds; the count is 45 stale. In #15903's scope, with a suggestion to drop the number rather than correct it, since "every entry below carries exactly three" needs no maintenance.Method note
Enumeration by
git ls-treeagainst merged base, rules transcribed from the shell source rather than sourced, comment lines excluded using the detector's own_HV_COMMENT_REshape. I deliberately did not count.md(219 files) or generated test-result JSON toward the finding: documentation naming a deployment path is not the same defect, and folding it in would have inflated a real result into an unusable one. The 32 are deployment configuration only.Verdict for this ratchet: staleness guarded, blindness unguarded, and blindness has a measured cost of 32 undecided findings across 18 files.
Next: file-size and commit-trailer.
mrveiss commentedon Sep 7, 2026
Re-derivation: the file-size ratchet. 47 oversized files are exempt and none of them appear in the exemption list.
Taking the file-size baseline re-derivation. The thesis holds here, measured.
What the detector can see
scripts/check_python_file_size.py:166-187enumerates withgit ls-files "*.py"minusEXCLUDED_PREFIXES:.worktrees/is right — those are not repository files. The other two are realtracked trees.
The measurement
The twelve largest:
One is 3.9x the ceiling.
KNOWN_LARGEexists to record every file allowedto exceed 600 lines, and 47 files exceed it with no entry — not because anyone
decided they should be exempt, but because the enumeration never offered them
for the decision. That is this issue's shape exactly: an exemption you can read
is a decision; an exemption caused by a blind spot is invisible.
The part that makes it more than an instance
autobot-backend/code_analysis/is the same tree #15915 just proved was darkto flake8 for six to nine months, hiding five constructors that raised
NameErroron every call. #15915's conclusion was that a guard bounded by theexclusion that caused the defect can only ever find the instance you already
knew about, and it deleted the F821 scope rather than widening it.
The file-size guard still carries the same exclusion, and two of its largest
invisible files —
anti_pattern_detector.pyat 2341 andenv_analyzer.pyat1686 — are in that tree. One exclusion string, propagated across guards, each
one independently reporting a clean tree.
The floor has the #15913 shape too
MIN_TRACKED_PY_FILES = 3000against 5241 in scope. Tested by removing eachtop-level tree:
The floor detects one failure: losing the tree holding 75% of the files. Its
docstring says it guards against "a walk that silently stopped reaching most of
the tree" — which is accurate, and "most" is doing the work. A walk that
stopped reaching five of six trees passes.
What I would do
Not "make 47 files shorter". The point is that the decision should be
visible:
already over the ceiling to
KNOWN_LARGEat its current size, in bothmirrors. That converts 47 invisible exemptions into 47 readable ones that
ratchet down like every other entry. It is a large diff and a boring one,
which is the correct shape for this.
autobot-infrastructure/is genuinely out of scope as deployment toolingrather than production code, say so in
KNOWN_LARGE's docstring as ascope statement, not as a prefix in the enumerator. The difference is that
a reader of the exemption list learns it either way.
MIN_TRACKED_PY_FILESnear the in-scope count, or add a per-treepresence assertion so losing any tree fails rather than only the largest.
Item 1 is the re-derivation this issue asks for. Items 2 and 3 are what stop it
being needed again.
Method note
Everything above is a second enumeration, deliberately not reusing the
detector's own code path —
git ls-files "*.py", the same prefix filter appliedindependently, and a line count per file. That is the point of a re-derivation:
a baseline checked with the instrument that produced it can only ever confirm
itself.
mrveiss commentedon Sep 7, 2026
Re-derivation 3 of 5 — file-size ratchet: passes, and it is the template
This is the counter-example the issue needs. The pattern is not inevitable, and this ratchet shows what the fixed version looks like.
The measurement
Enumerated
git ls-files "*.py"independently, counted lines myself, parsedKNOWN_LARGEout of the source rather than importing it, and compared both directions:.pyKNOWN_LARGEentriesExact congruence, both directions. No blind spot inside the scope, and no stale entries either — so this one satisfies both the staleness and the blindness direction, which none of the others do.
Why it passes, and it is not luck
The blindness was here, was severe, and was found.
check_python_file_size.py:34-40records it:That is this issue's thesis, in the repository's own words, discovered independently at #14547 and fixed by exactly the remedy proposed here: derive the population from the tree instead of from the list.
autobot-slm-backend/services/reconciler.pysurfaced it at over 2,000 lines and absent from the list entirely.So the fix is not hypothetical and its cost is known: grandfathering 509 files at measured size, then ratcheting.
The refinement this forces on the issue
47
.pyfiles over 600 lines sit outside the walk —autobot-infrastructure/(330 files excluded),autobot-backend/code_analysis/,.worktrees/. The largest is 2,341 lines.That is not blindness, and the difference is the useful part. It is:
EXCLUDED_PREFIXESat:99-103, three literal prefixes;exclude:in.pre-commit-config.yaml, so the audit's scope and the staged-file scope are the same set (file-size guard: several autobot-slm-backend/services/*.py exceed 600 lines with no grandfather entry #14547);test_excluded_prefixes_mirror_the_pre_commit_configfails if the two drift apart.So a reader can discover the boundary by reading it, and the boundary cannot move silently.
That sharpens the issue's claim. The defect is not "the baseline's population is narrower than the tree's" — every guard is scoped. It is "the boundary is invisible, so absence and cleanliness are indistinguishable." Compare:
EXCLUDED_PREFIXESHV_SCAN_EXTENSIONSexists, but nothing says systemd units are therefore unexaminedinline_genericsI'd amend AC 4 to read: the population boundary is stated where a reader meets the baseline, and a test fails when the boundary moves. Exact congruence is the outcome; a declared, guarded boundary is the mechanism.
Whether
autobot-infrastructure/should stay excluded is a separate question worth someone's decision — 47 files over the limit, one at 2,341 lines, in a tree the ratchet does not constrain at all — but it is a scope question, not a defect, and it should not ride on this issue.One correction, recorded because it is this issue's own failure mode
My first run reported 497 over-limit files with no baseline entry — a catastrophic-looking finding. It was my bug:
KNOWN_LARGEis annotated (KNOWN_LARGE: dict[str, int] = {...}), so it parses asast.AnnAssign, and I only handledast.Assign. My parse returned zero entries, so every file read as missing.The tell was the number: 497 missing and 497 over-limit are the same number, which is what "compared against an empty set" looks like. A re-derivation is an instrument too, and mine needed the same non-vacuity check I have been asking of everyone else's — a floor asserting the baseline parsed to more than zero entries would have caught it before I looked at the result at all.
Next: re-derivation 4, the commit-trailer baseline.
mrveiss commentedon Sep 7, 2026
Re-derivation 4 of 5 — commit-trailer baseline: 4 entries, 7,087 commits
The largest gap of the four, and the one where the remedy is not to close it.
The measurement
Replicated the gate's own logic against full history rather than a PR range —
agent_re,coauthor_re, and both exemptions (owner_re,bot_ident_re) transcribed from.github/workflows/no-commit-trailers.yml:79-95, applied line-anchored:Dev_new_guiThe identities are the banned forms, not false positives —
Co-Authored-By: Claude(2,024),Claude Sonnet 4.6(1,550 + 1,285),Claude Opus 4.5(1,481),Paperclip(1,239 + 791).github-actions[bot](757) anddependabot[bot](470) are correctly exempted bybot_ident_reand are excluded from the 6,206.Why this is not a bug report
The gate is right and should not change. It runs on
pull_requestand iteratesgit rev-list "${BASE_SHA}..${HEAD_SHA}"— new commits only. A forward ratchet is the correct design, and the alternative, rewriting 12,786 commits, is worse than the problem.So the 7,083 are not outstanding work. The finding is what the number 4 communicates.
The invisible boundary, in its clearest form
The baseline's own header:
A reader meets a four-entry file, described as a ratchet that only shrinks, and takes away that the repository has four outstanding trailer violations and is nearly clean. The true reading is: four commits tripped the gate while it was running and were exempted rather than fixed. It is a count of gate events, not of tree state, and the two differ by three orders of magnitude.
The workflow header says "Fails a PR whose commits carry…", so the mechanism is stated. What is not stated anywhere is the consequence — that history was never examined, and that the baseline's size is therefore unrelated to how much of the tree carries banned trailers.
This is re-derivation 3's distinction at its widest. Compare:
EXCLUDED_PREFIXES, guarded by a mirror testHV_SCAN_EXTENSIONS; systemd units unreachableinline_genericsWhat I'd actually change
Nothing in the gate. One paragraph in the baseline file, saying what the count is a count of:
That is the cheapest possible instance of the AC 4 amendment from re-derivation 3: state the boundary where a reader meets the baseline. Here it costs one comment block and converts a badly misleading number into an honestly narrow one.
Method note
My first attempt reported 3,444 and 8,010 — both wrong. I had flattened the log with
tr -d '\n'before matching, which destroyed the^[[:space:]]*anchoring the gate's regexes depend on, and I had not applied the owner or bot exemptions at all. The numbers were large and plausible and I nearly wrote them up. A re-derivation that does not replicate the original's exemptions is not measuring the same predicate — it is answering a looser question and reporting the answer as if it were the same one.That completes the five ratchets — file-size, hardcoded-values,
inline_generics, commit-trailer, plus the #15896 citations that started this. Four of the five have a boundary a reader cannot see; the one that passes is the one that declares and guards it.mrveiss commentedon Sep 7, 2026
ACs 4 and 5 — the doc, ready to commit verbatim
Path:
docs/developer/RATCHET_BASELINES.md(new file, 174 lines).Not folded into
CANONICAL_RULES.md: that file is a catalogue ofcanonical_check*.pyrules with a fixed per-rule schema, and this is a convention rather than a rule with aRULE_ID.One line for the trigger table in
CLAUDE.md, so authors meet it where AC 4 requires:AC 4 as amended (per re-derivation 3): the criterion is not exact congruence — every guard is scoped and a narrow scope is not the defect. It is that the boundary is stated where a reader meets the baseline, and a test fails when the boundary moves. Rules 1 and 2 carry that.
AC 5 is served by two worked examples rather than one: file-size at 497 vs 497 as the case that passes, and #15896's 8 vs 26 as the failure. The counter-example is the load-bearing half — a rule with only failures reads as "be careful"; a rule with a passing instance shows the shape to copy.
Rules 5 and 6 are the two failures I hit while doing this work, kept because they are the ones most likely to catch whoever follows the page.
docs(guards): say why the wrong number looked right (#15897)
docs(guards): bind rule 6 to a known positive, and stop claiming five…
docs(guards): label every evidence count with its scope and predicate (…
docs(guards): a surviving mutant is a claim to check, not a result (#…
docs(guards): not every ratchet starts empty, and a control is not a …
docs(guards): write down the ratchet-baseline convention and its work…
mrveiss commentedon Sep 7, 2026
Acceptance criteria 4 and 5 are met by PR #15959, merged (
d9c8fbd082). Verifiedagainst
origin/Dev_new_guiafter the merge.AC4 — the convention is written where ratchet authors will meet it, with the
two-directions table and the compare-sets-not-counts rule.
docs/developer/RATCHET_BASELINES.md, 257 lines on merged base. Structure:CLAUDE.mdcarries one trigger-table row pointing at it (1 occurrence on base),per that file's lean-instructions rule.
AC5 — a worked example is linked so the next author sees a real 8-vs-26.
Both are present:
#15896's 8-vs-26 as the failure shape, and the file-sizeratchet at 497-vs-497 as the counter-example. The passing case was the load-bearing
half — a rule with only failures reads as "be careful"; a rule with a passing
instance shows the shape to copy.
AC1–AC3 were satisfied by the five re-derivations recorded on this issue
before the PR existed, and the deltas are stated there.
What the review changed, because it bears on the page's own subject
The document contained the defect it describes, three times, and each was caught
by a reader rather than by its authors:
more than zero entries" — contradicting the page's own checklist six lines
below and failing any ratchet whose population is legitimately empty. Replaced
with a known-positive control, which is a claim about the instrument and
holds at any size including zero.
read
congruent by construction, which tests nothing about blindness. Nowfour, with the fifth marked outstanding rather than counted as evidence.
2,426 · 7,087 · 497 · 8 · 26werepresented without the revision, file set, or matching rule that makes them
checkable — on a page whose subject is numbers that do not carry their frame.
## How to read the numbers on this pagenow binds each cluster to itspredicate, and states which clusters are deliberately not comparable.
A further correction came from a peer who re-derived every figure independently:
counting the
7,373/5,033occurrence totals as commits gives 12,408,and only the predicate table prevented that being reported as an error in the
page. The table is doing work, not decoration.
github-actions commentedon Sep 7, 2026
PR #15959 (merged to
Dev_new_gui) references this issue with a close keyword.If this issue is fully resolved, close it manually. If work remains, no action is needed.
fix(guards): match git's command structure, and stop the census exemp…
guards(primitive): git-side exclusions on tracked_paths, and a shrink…
guard(size): honour the one root spelling, and let the floor do the r…
guard(size): count the oversized files the size hook's exclusions hide (
mrveiss commentedon Sep 8, 2026
#16068 merged — the file-size half is delivered. Staying open for the other half.
Verified against
origin/Dev_new_gui, not the PR diff:EXCLUDED_TREE_DEBT = 47and both tests present inrepo_tests/excluded_tree_size_debt_test.py.What this issue asked for, and what landed
The thesis was that a ratchet baseline is produced by its own detector, so
whatever the detector cannot see becomes permanently exempt without appearing
in the list whose purpose is recording exemptions.
The file-size instance is now measured and pinned: 331 files in
autobot-infrastructure/andautobot-backend/code_analysis/, 47 of themover the 600-line ceiling, largest at 2,341 — none of which could ever appear
in
KNOWN_LARGE, because the enumeration removed them before the mapping wasasked.
The design changed once I read the code, and my own recommendation on this
issue was the thing that was wrong. I had proposed adding all 47 to
KNOWN_LARGE. Two rulings say otherwise: that mapping's docstring states itonly ever shrinks and no entry may be added to make a file pass, and
EXCLUDED_PREFIXESis mirrored in.pre-commit-config.yamland pinned by atest — so narrowing it widens the pre-commit hook for every committer, which
is a scope decision rather than a visibility one.
So the debt is counted rather than grandfathered. The reviewer's argument for
that is stronger than mine and belongs here: conflating the two does not merely
blur a distinction, it moves 47 files from recorded to unrecorded in the
register that exists to record them, which is this issue's own thesis pointed
at a boundary instead of a detector.
Still open
this was filed. Untouched.
MIN_TRACKED_PY_FILES = 3000incheck_python_file_size.py, against5,613 in-scope files. Deliberately not fixed in guard(size): count the oversized files the size hook's exclusions hide (#15897) #16068: same issue, same
finding, but production code with a different blast radius. It belongs
with guards(primitive): declare() must refuse a floor below its own discovery — all 7 floors reviewed this week were set low #15928's floor campaign — where guard(reach): one measured TRACKED_PY_FLOOR instead of seven copies of 3000 (#15928) #16073 has since consolidated seven
copies of a different 3000, and this one is the eighth of that shape,
not of this one.
re-derived by a second path — which is guards(primitive): baselines record their provenance and are re-derived by a second path #15929, this issue's child.
One measurement worth leaving for whoever takes the rest
The 47 is stable across base movement: none of the five commits on base at merge
time touched
*.pyin either excluded tree, and an independent count on currentbase returns 47. The equality pin makes this issue's guard sensitive to base
changes in those trees even with no file overlap in a diff, which a standard
overlap check does not catch — worth knowing before the next PR that touches
either tree.
ci(duplication-guard): scan the three trees it never opened (#13044) (#…
mrveiss commentedon Sep 12, 2026
Stale-issue sweep (never auto-closed — Dev_new_gui wasn't GitHub's default branch at merge time). Verified against current
origin/main; leaving open.The delivering PR (#15959) states in its own body that it delivers only ACs 4-5 (documenting the convention + a worked example) —
docs/developer/RATCHET_BASELINES.md:26-30,231-241.NOT MET: AC1 (independent re-derivation of all 5 baselines) — the doc itself lists hardcoded-values as "not independently re-derived" (lines 11-13). AC2 (deltas corrected) —
frontend_api_contract_ratchet_test.py:80,95still hardcodes577/87despite the doc noting the real deltas (573 vs 577, commit-trailer 4 vs 7,087) as uncorrected. AC3 (re-derived number substituted into the test) — never done;573never replaces577in the file.mrveiss commentedon Sep 20, 2026
Re-verification against current
origin/main(2026-09-20). Tracked branchissue-15897-ratchet-baselinesis already gone from origin.Substantial analysis done, corrections partial —
docs/developer/RATCHET_BASELINES.mdis the real, thorough artifact this issue asked for, tracking itself by number:AC1 (independent re-derivation) — 4 of 5 done, 1 not started:
inline_generics: 577 vs 573 — re-derived, confirms the counter is provably defective (bug(ci): the inline_generics ratchet counts comments, so documenting the fix fails the build #15771: counts matches inside comments), matching this issue's own prediction that it was the known-bad one.AC2 (correct any baseline that differs, state the delta) — partial. The citations baseline (8→26) was corrected. commit-trailer and
inline_genericsare NOT corrected — both differences are documented with real numbers, but no baseline-file change has landed for either.AC3 (
inline_genericsre-derived with comments stripped, or #15771 fixed first) — not done. #15771 is still OPEN; the 577-vs-573 finding exists but the counter itself hasn't been fixed and the baseline hasn't been regenerated.AC4 (convention documented) — done.
RATCHET_BASELINES.mdstates the two-directions rule, the "compare sets, not counts" rule, and a dedicated section ("the second implementation must replicate the predicate, exemptions included") built directly from the commit-trailer near-misses.AC5 (worked example linked) — done. The 8-vs-26 citations case is documented in full, including how it was caught.
2 of 5 ACs fully done (documentation + worked example), the re-derivation analysis itself is 80% complete with real findings, but the corrections that analysis should produce (AC2/AC3) are mostly still pending — inline_generics is blocked on #15771, commit-trailer's undeclared-boundary finding hasn't been resolved into a fix, and hardcoded-values hasn't been touched. Leaving open.
fix(guards): correct two ratchet baselines and close the zero-backlog…
chore(vehicle): v0.9.1 — consolidate 6 ready PRs into one CI run (#17194