Skip to content

feat(ci): automate ratchet shrink-banking so caps stop outliving their files (#8584) - #8612

Merged
diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.49from
MumuTW:chore/auto-bank-ratchet-shrinks
Jul 27, 2026
Merged

diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.49from
MumuTW:chore/auto-bank-ratchet-shrinks

Conversation

@MumuTW

@MumuTW MumuTW commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Implements proposal 2 of #8584. Companion to #8585 (the one-off banking run) and #8589 (the --update gap fix) — but this is the one that makes the other two stop decaying.

The problem, restated in one line

The ratchet is only half automatic, and it is the wrong half.

direction cost who does it
raise a cap a ten-second JSON edit, and the fastest way to unblock a red PR anyone, under merge pressure
lower a cap run --update, commit, get it reviewed nobody — no workflow invokes it

Grep .github/workflows/ for --update: wiki-sync.yml (unrelated) and ci.yml's check-quality-ratchet.mjs --require-tighten (a different script, a different metric). Neither check:file-size --update nor check:complexity-ratchets --update is run by anything.

Measured on release/v3.8.49 at 4053e2314: 18 frozen files already at or under the 800-line new-file cap, worst at 132× (src/shared/validation/schemas.ts, 19 lines carrying a 2,523 cap); the complexity ceiling walked 1794 → 2169 across ~37 rebaseline notes with exactly one decrease (−1); "tighten via --update next cycle" written 31 times, honoured once in six weeks.

The consequence is not "caps drift up". It is that a cap outlives the code that earned it, so every completed decomposition silently becomes a growth allowance for whoever edits that file next.

What this adds

New job bank-ratchet-shrinks in nightly-release-green.yml — that workflow already measures the active release branch, so this reuses its lane, its branch resolution, and its injection guard.

resolve highest release/vX.Y.Z
  → check:file-size --update
  → check:complexity-ratchets --update
  → verify-ratchet-bank.mjs      ← aborts here if anything moved the wrong way
  → force-update one branch, open/refresh ONE PR

Schedule + dispatch only, deliberately not push. Banking has no latency requirement — a shrink banked within 8h is fine — while a per-merge run would rebuild the PR branch repeatedly during merge campaigns and pay for a full ESLint walk each time. Detection stays on push (release-green, unchanged); only banking is batched.

It never pushes to release/*. A human merges the PR, so a bad measurement cannot land unreviewed. That is also why it opens one force-updated PR rather than one per run.

The safety verifier is the actual argument

A bot that could raise a cap would be strictly worse than the status quo. So the job writes nothing unattended without scripts/quality/verify-ratchet-bank.mjs clearing it first. The verifier diffs the post---update tree against HEAD and aborts the job before a commit exists — opening no PR — unless every single change is one of:

  • a frozen / testFrozen numeric entry lowered or removed
  • complexity-baseline.json → count lowered
  • quality-baseline.json → metrics.cognitiveComplexity.value lowered

Everything else fails: raising a number, adding an entry, changing cap/testCap, or deleting/rewriting a _rebaseline_* note. That last one matters more than it looks — the notes are the audit trail for why each ceiling exists, and they are stored inside the same frozen object as the file entries (which is why the gate prints 182 arquivos congelados for 127 real files). A naive "rewrite the JSON" bot would eat them.

Verified both directions against the real baselines

Forward — --update then verify, on the actual release tip:

[file-size] baseline ratcheado: 74 arquivo(s) encolheram
[test-file-size] baseline ratcheado: 17 arquivo(s) de teste encolheram
[verify-ratchet-bank] OK — 77 lowered, 14 removed, nothing raised.   exit=0

Negative control — hand-raise one entry and the cap, exactly what the verifier exists to stop:

[verify-ratchet-bank] UNSAFE — 2 problem(s); refusing to bank:
  ✗ file-size: "cap" changed — only the ratcheted values may move
  ✗ frozen: open-sse/handlers/chatCore.ts RAISED 5125 → 9999
  → the banking lane may only lower or remove.                       exit=1

The baseline edits were reverted — this PR contains no baseline changes and no product code changes. Banking the current 91 is #8585's job; this is the machinery that keeps doing it.

tests/unit/verify-ratchet-bank.test.ts — 22 tests, one per way the automation could go wrong (raise, add, delete a note, rewrite a note, move cap, move an unrelated metric while the tracked one legitimately dropped, …). All pass.

Notes for review

  • docs/architecture/QUALITY_GATES.md shows ~29 deleted lines that are not mine. lint-staged runs prettier --write on *.md, and two pre-existing tables in that file were not prettier-clean at HEAD, so any commit touching the file reformats them. The change is whitespace-only. Say the word and I will add the file to .prettierignore instead (there is precedent — docs/reference/ENVIRONMENT.md is excluded for exactly this reason).
  • This gets strictly more effective once fix(quality): let --update remove baseline entries that already fit the cap #8589 merges. --update currently cannot remove an entry whose value already equals the frozen value and sits under the cap; fix(quality): let --update remove baseline entries that already fit the cap #8589 adds that third bucket. The lane inherits whatever --update can do.
  • --update is shrink-only by construction, independently of this PR: check-file-size.mjs writes only from improvements, and check-complexity-ratchets.mjs only when .improved. The verifier is defence in depth, not the only guard.
  • Both --update invocations run under set +e on purpose: they exit non-zero while the branch is over baseline, which is precisely when there is nothing to bank. Their exit code is not the signal; the verifier is.

Closes the mechanism half of #8584. Proposal 3 (replacing the 93 dead #3501 references with a live decomposition tracker) is not in this PR.

MumuTW added 2 commits July 26, 2026 17:51
…per-inference#8584)

The quality ratchet is only half automatic, and it is the wrong half. Raising a
cap is a manual JSON edit that takes ten seconds and is the fastest way to unblock
a red PR. Lowering one requires someone to run `--update` and commit the result —
and no workflow does: grepping `.github/workflows/` for `--update` finds only
wiki-sync.yml (unrelated) and ci.yml's check-quality-ratchet.mjs --require-tighten
(a different script against a different metric).

Measured on release/v3.8.49 at 4053e23: 18 frozen files already at or under the
800-line new-file cap, the worst at 132x (src/shared/validation/schemas.ts, 19
lines carrying a 2,523 cap); the complexity ceiling walked 1794 -> 2169 across ~37
rebaseline notes with exactly one decrease (-1); "tighten via --update next cycle"
written 31 times and honoured once in six weeks. A cap that outlives the code that
earned it converts every completed decomposition into a growth allowance for
whoever edits that file next.

New job `bank-ratchet-shrinks` in nightly-release-green.yml measures the active
release branch, runs the two shrink-only `--update` paths, and opens ONE
always-current PR with the result. Schedule/dispatch only, deliberately not on
push: banking has no latency requirement, while a per-merge run would rebuild the
PR branch during merge campaigns and pay for a full ESLint walk each time.
Detection stays on push (release-green); only banking is batched. The job never
pushes to release/* — a human merges, so a bad measurement cannot land unreviewed.

scripts/quality/verify-ratchet-bank.mjs is the hard guarantee that the automation
can only ever write downward. It diffs the post-`--update` tree against HEAD and
aborts the job before a commit exists — opening no PR — unless every change is a
frozen/testFrozen entry lowered or removed, `count` lowered, or
cognitiveComplexity.value lowered. Raising a number, adding an entry, changing
cap/testCap, or deleting/rewriting a `_rebaseline_*` note all fail. A bot that
could raise a cap would be strictly worse than the status quo.

Verified both directions against the real baselines: --update + verifier reports
77 lowered / 14 removed / nothing raised, exit 0; hand-raising chatCore.ts to 9999
and cap to 1200 is rejected with exit 1. 22 unit tests cover each way the
automation could go wrong.

No product code changes.
@MumuTW
MumuTW force-pushed the chore/auto-bank-ratchet-shrinks branch from 2aa6bab to 8bef1c9 Compare July 26, 2026 09:51
@diegosouzapw
diegosouzapw merged commit b676c5f into cheaper-inference:release/v3.8.49 Jul 27, 2026
15 checks passed
diegosouzapw added a commit that referenced this pull request Jul 27, 2026
…stryker drift

file-size: nine frozen entries could not absorb the combined result of the
31-PR train. Two distinct causes, kept apart in the baseline note on purpose:

  (1) GENUINE irreducible growth at existing chokepoints —
      providerLimits/auth (#8632), rateLimitManager (#8616),
      models-catalog-route.test (#8610).
  (2) COLLISION with #8585, which banked shrinks measured on the pre-train
      release tip while 30 sibling PRs in the SAME train grew those files
      again — chat/accountFallback (#8628), chatCore (#8613),
      videoGeneration (#8581), imageGeneration.

Ceilings re-pinned to the post-merge tip. #8612 (also in this train) automates
shrink-banking so this self-inflicted drift stops recurring.

stryker: three covering unit tests were missing from tap.testFiles —
isLocalStreamLifecycleError-abort-shape (circuitBreaker.ts, a shared base-red
that was reddening Fast Quality Gates on every open PR),
noauth-autocombo-lockout-7623 (accountFallback.ts) and
kimi-quota-reset-recovery (auth.ts), the latter two landed with this train.
@diegosouzapw diegosouzapw mentioned this pull request Jul 28, 2026
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…r files (cheaper-inference#8584) (cheaper-inference#8612)

* feat(ci): automate ratchet shrink-banking on the release branch (cheaper-inference#8584)

The quality ratchet is only half automatic, and it is the wrong half. Raising a
cap is a manual JSON edit that takes ten seconds and is the fastest way to unblock
a red PR. Lowering one requires someone to run `--update` and commit the result —
and no workflow does: grepping `.github/workflows/` for `--update` finds only
wiki-sync.yml (unrelated) and ci.yml's check-quality-ratchet.mjs --require-tighten
(a different script against a different metric).

Measured on release/v3.8.49 at d408a20: 18 frozen files already at or under the
800-line new-file cap, the worst at 132x (src/shared/validation/schemas.ts, 19
lines carrying a 2,523 cap); the complexity ceiling walked 1794 -> 2169 across ~37
rebaseline notes with exactly one decrease (-1); "tighten via --update next cycle"
written 31 times and honoured once in six weeks. A cap that outlives the code that
earned it converts every completed decomposition into a growth allowance for
whoever edits that file next.

New job `bank-ratchet-shrinks` in nightly-release-green.yml measures the active
release branch, runs the two shrink-only `--update` paths, and opens ONE
always-current PR with the result. Schedule/dispatch only, deliberately not on
push: banking has no latency requirement, while a per-merge run would rebuild the
PR branch during merge campaigns and pay for a full ESLint walk each time.
Detection stays on push (release-green); only banking is batched. The job never
pushes to release/* — a human merges, so a bad measurement cannot land unreviewed.

scripts/quality/verify-ratchet-bank.mjs is the hard guarantee that the automation
can only ever write downward. It diffs the post-`--update` tree against HEAD and
aborts the job before a commit exists — opening no PR — unless every change is a
frozen/testFrozen entry lowered or removed, `count` lowered, or
cognitiveComplexity.value lowered. Raising a number, adding an entry, changing
cap/testCap, or deleting/rewriting a `_rebaseline_*` note all fail. A bot that
could raise a cap would be strictly worse than the status quo.

Verified both directions against the real baselines: --update + verifier reports
77 lowered / 14 removed / nothing raised, exit 0; hand-raising chatCore.ts to 9999
and cap to 1200 is rejected with exit 1. 22 unit tests cover each way the
automation could go wrong.

No product code changes.

* chore(skills): sync cli-backup-sync SKILL.md after rebase onto tip
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…stryker drift

file-size: nine frozen entries could not absorb the combined result of the
31-PR train. Two distinct causes, kept apart in the baseline note on purpose:

  (1) GENUINE irreducible growth at existing chokepoints —
      providerLimits/auth (cheaper-inference#8632), rateLimitManager (cheaper-inference#8616),
      models-catalog-route.test (cheaper-inference#8610).
  (2) COLLISION with cheaper-inference#8585, which banked shrinks measured on the pre-train
      release tip while 30 sibling PRs in the SAME train grew those files
      again — chat/accountFallback (cheaper-inference#8628), chatCore (cheaper-inference#8613),
      videoGeneration (cheaper-inference#8581), imageGeneration.

Ceilings re-pinned to the post-merge tip. cheaper-inference#8612 (also in this train) automates
shrink-banking so this self-inflicted drift stops recurring.

stryker: three covering unit tests were missing from tap.testFiles —
isLocalStreamLifecycleError-abort-shape (circuitBreaker.ts, a shared base-red
that was reddening Fast Quality Gates on every open PR),
noauth-autocombo-lockout-7623 (accountFallback.ts) and
kimi-quota-reset-recovery (auth.ts), the latter two landed with this train.
@MumuTW
MumuTW deleted the chore/auto-bank-ratchet-shrinks branch August 27, 2026 01:47
MumuTW added a commit to MumuTW/OmniRoute that referenced this pull request Aug 27, 2026
…ting every nightly bank

`verifyFrozenMap` compared `_rebaseline_*` note values with `after[key] !== prev`.
That is a referential comparison: correct for the string notes it was written
against, wrong for the object-valued one that landed on 2026-08-11, where the
merge-storm rebaseline folded four file->LOC pairs under a single note key. Two
structurally identical objects off two `JSON.parse` calls are never `===`, so the
verifier reported that note "rewritten" on a tree nobody had touched.

Effect: `node scripts/quality/verify-ratchet-bank.mjs` exited 1 on pristine
release/v3.8.51, and the `bank-ratchet-shrinks` job in nightly-release-green.yml
(cheaper-inference#8612) aborts on any non-zero exit. The automation added precisely to stop caps
outliving their shrinks (cheaper-inference#8584) has therefore banked nothing since 2026-08-11.

Fix uses the module's own `jsonEqual()` helper, so notes are still compared
verbatim -- a genuinely edited note, string or object, is still rejected. Both
new tests fail without the one-line change and pass with it.

Claude-Session: https://claude.ai/code/session_01WXi3P7fMBXdVyZdnEeDYwq
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…r files (cheaper-inference#8584) (cheaper-inference#8612)

* feat(ci): automate ratchet shrink-banking on the release branch (cheaper-inference#8584)

The quality ratchet is only half automatic, and it is the wrong half. Raising a
cap is a manual JSON edit that takes ten seconds and is the fastest way to unblock
a red PR. Lowering one requires someone to run `--update` and commit the result —
and no workflow does: grepping `.github/workflows/` for `--update` finds only
wiki-sync.yml (unrelated) and ci.yml's check-quality-ratchet.mjs --require-tighten
(a different script against a different metric).

Measured on release/v3.8.49 at 54b4bf1: 18 frozen files already at or under the
800-line new-file cap, the worst at 132x (src/shared/validation/schemas.ts, 19
lines carrying a 2,523 cap); the complexity ceiling walked 1794 -> 2169 across ~37
rebaseline notes with exactly one decrease (-1); "tighten via --update next cycle"
written 31 times and honoured once in six weeks. A cap that outlives the code that
earned it converts every completed decomposition into a growth allowance for
whoever edits that file next.

New job `bank-ratchet-shrinks` in nightly-release-green.yml measures the active
release branch, runs the two shrink-only `--update` paths, and opens ONE
always-current PR with the result. Schedule/dispatch only, deliberately not on
push: banking has no latency requirement, while a per-merge run would rebuild the
PR branch during merge campaigns and pay for a full ESLint walk each time.
Detection stays on push (release-green); only banking is batched. The job never
pushes to release/* — a human merges, so a bad measurement cannot land unreviewed.

scripts/quality/verify-ratchet-bank.mjs is the hard guarantee that the automation
can only ever write downward. It diffs the post-`--update` tree against HEAD and
aborts the job before a commit exists — opening no PR — unless every change is a
frozen/testFrozen entry lowered or removed, `count` lowered, or
cognitiveComplexity.value lowered. Raising a number, adding an entry, changing
cap/testCap, or deleting/rewriting a `_rebaseline_*` note all fail. A bot that
could raise a cap would be strictly worse than the status quo.

Verified both directions against the real baselines: --update + verifier reports
77 lowered / 14 removed / nothing raised, exit 0; hand-raising chatCore.ts to 9999
and cap to 1200 is rejected with exit 1. 22 unit tests cover each way the
automation could go wrong.

No product code changes.

* chore(skills): sync cli-backup-sync SKILL.md after rebase onto tip
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…stryker drift

file-size: nine frozen entries could not absorb the combined result of the
31-PR train. Two distinct causes, kept apart in the baseline note on purpose:

  (1) GENUINE irreducible growth at existing chokepoints —
      providerLimits/auth (cheaper-inference#8632), rateLimitManager (cheaper-inference#8616),
      models-catalog-route.test (cheaper-inference#8610).
  (2) COLLISION with cheaper-inference#8585, which banked shrinks measured on the pre-train
      release tip while 30 sibling PRs in the SAME train grew those files
      again — chat/accountFallback (cheaper-inference#8628), chatCore (cheaper-inference#8613),
      videoGeneration (cheaper-inference#8581), imageGeneration.

Ceilings re-pinned to the post-merge tip. cheaper-inference#8612 (also in this train) automates
shrink-banking so this self-inflicted drift stops recurring.

stryker: three covering unit tests were missing from tap.testFiles —
isLocalStreamLifecycleError-abort-shape (circuitBreaker.ts, a shared base-red
that was reddening Fast Quality Gates on every open PR),
noauth-autocombo-lockout-7623 (accountFallback.ts) and
kimi-quota-reset-recovery (auth.ts), the latter two landed with this train.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants