Skip to content

fix(ci): collapse dual lint gates into one ratcheting budget - #1059

Merged
RUKAYAT-CODER merged 2 commits into
rinafcode:mainfrom
snowrugar-beep:fix/issue-1017-single-lint-gate
Aug 27, 2026
Merged

fix(ci): collapse dual lint gates into one ratcheting budget#1059
RUKAYAT-CODER merged 2 commits into
rinafcode:mainfrom
snowrugar-beep:fix/issue-1017-single-lint-gate

Conversation

@snowrugar-beep

@snowrugar-beep snowrugar-beep commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #1017
Closes #1016

Closes #1018
Closes #1019

Collapses the two conflicting ESLint gates into exactly one, backed by a single ratcheting warning budget in lint-budget.json. The CI Lint step now reads its ceiling from that file instead of a hardcoded 250, and syntax.yml loses its duplicate --max-warnings=0 ESLint step (it keeps the type check). A new npm run lint:budget check fails both when warnings exceed the budget and when the committed budget is looser than the measured count, so the ceiling can only decrease; npm run lint:budget:record measures and tightens it after a cleanup. The single most important design decision is to store the budget in a committed file that CI reads, making it one source of truth that a ratchet drives strictly downward.

Why

ci.yml allowed 250 lint warnings while syntax.yml allowed zero — two gates on the same tool with thresholds 250 apart, so one was always theatre. The 250 cap was hardcoded in the workflow, had no ratchet, and the duplicate gate made the real policy unknowable. This change removes the contradiction, expresses the policy once, and ties it to a measured, decreasing budget.

What was built

  • .github/workflows/syntax.yml — removed the duplicate ESLint check (no-fix) step (--max-warnings=0). The workflow is now type-check only, so exactly one workflow gates on lint.
  • .github/workflows/ci.yml — the Lint step now derives its ceiling from lint-budget.json (--max-warnings=$(node -p "require('./lint-budget.json').maxWarnings")) instead of hardcoding 250, and a new Enforce lint budget ratchet step runs npm run lint:budget.
  • lint-budget.json (new) — single source of truth: { "maxWarnings": 250 }. The initial value matches the previous hardcoded ceiling so the first run is behavior-identical; the ratchet replaces it with a measured count.
  • scripts/lint-budget.js (new) — does not reinvent ESLint; it shells out to the existing npx eslint, parses the JSON output, and counts severity === 1 messages. check fails when warnings exceed the budget or when the budget is looser than the measured count (forcing record); record refuses to raise the budget and writes the measured count back. Config errors surface as exit 2.
  • package.json — added lint:budget and lint:budget:record.
  • CONTRIBUTING.md — documented the budget file, both commands, and the ratchet rule.

Files modified: 4; new files: 2 (lint-budget.json, scripts/lint-budget.js).

Acceptance criteria coverage

  • Exactly one workflow gates on lint (the duplicate ESLint step removed from syntax.yml; only ci.yml runs lint)
  • The warning budget is measured, not guessed (the budget is enforced by scripts/lint-budget.js, which measures the live warning count; the committed 250 is the pre-existing ceiling and the ratchet check fails until a measured value is recorded via npm run lint:budget:record)
  • The budget can only decrease (record refuses to raise the budget; check fails when the committed budget is looser than the measured count)

Deliberately deferred

  • The current measured warning count was not hardcoded here. The initial budget matches the existing 250 ceiling so behavior is unchanged on first run; the first npm run lint:budget / CI run forces lint:budget:record to capture the true count, which is the honest way to establish the number without fabricating it. This also inherently means the lint gate will keep failing until the recorded budget matches reality — consistent with the issue's premise that neither gate passes today. No code change is required afterward beyond one record commit.

Test plan

  • node --check scripts/lint-budget.js — JS syntax OK
  • lint-budget.json parses (node -e "require('./lint-budget.json')") — OK
  • npm run lint:budget — not executed locally (per program instructions to skip build/lint/test execution on this host; requires node_modules which is not installed here). The script's behavior is unit-readable: JSON parse of ESLint output, severity-1 count, two failure branches.
  • Manual (on a host with dependencies): npm run lint:budget should report over/under budget count, and npm run lint:budget:record should write the measured count to lint-budget.json.

Env vars / Notes

No environment variables introduced. The budget file lint-budget.json is the only config surface and must be committed with every tightening.

Remove the second ESLint gate from syntax.yml so exactly one workflow
gates on lint. The remaining lint gate in ci.yml now reads its warning
ceiling from a single lint-budget.json instead of a hardcoded 250, and a
new lint:budget check fails both when warnings exceed the budget and when
the committed budget is looser than the measured count, so the ceiling
can only decrease. lint:budget:record measures and tightens the budget
after a cleanup.
@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@snowrugar-beep Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants