Skip to content

fix(gates): catch lint and type errors before push, not in CI - #1620

Merged
cursor[bot] merged 24 commits into
mainfrom
claude/review-open-prs-fewxlh
Aug 5, 2026
Merged

fix(gates): catch lint and type errors before push, not in CI#1620
cursor[bot] merged 24 commits into
mainfrom
claude/review-open-prs-fewxlh

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Add a pre-push static gate (lint + typecheck). Two open PRs burned full CI cycles this week on defects one local command would have caught: Ship soft Category filter (replace phone select blue highlight) #1606 on a react-hooks/set-state-in-effect lint error, Phone Choose mode sheet: review + two large YES mockups #1618 on TS2339 for mode.devOnly (a union member that lacks the property — src/lib/app-modes.ts:400 already exports the correct "devOnly" in mode guard). Neither lint nor typecheck was in the pre-push path. guard-push.mjs gains a fourth guard covering both.
  • Fix the typecheck gate it depends on (ledger #210). Typecheck could not simply be added, because it was already unusable. tsconfig.json's include carries .next/types/**/*.ts and .next/dev/types/**/*.ts — gitignored build artifacts — so deleting a page leaves the stale generated validator importing a removed module. New tsconfig.typecheck.json + npm run typecheck:source answer "is the source sound?" deterministically.
  • Correct a doc claim that made Add three Clinical KB guide wireframe mockups #1580 surprising. "Mockups are exempt" was being read as blanket. They are exempt from the wiring and reachability gates and nothing else — still typechecked, and their client chunks still count toward check:bundle-budget.
  • Record the unmade decision behind Add three Clinical KB guide wireframe mockups #1580 as #237. The budget totals every built chunk, which contradicts ledger #013's "not an initial production bundle" position. Recorded rather than papered over; no metric was changed here.

Root cause was reproduced, not inferred. A planted .next/dev/types/validator.ts referencing a removed mockup page yields error TS2307: Cannot find module '.../mockups/deleted-mockup-route/page.js' — base config exit 2, source-only config exit 0. Red locally and green in CI (fresh checkout, no .next) is how the gate got abandoned, which is how a real type error then reached CI.

Verified against real build artifacts, not just the synthetic fixture: after npm run build, .next/types/validator.ts exists and is absent from the source config's file list. The only .next file that still enters is routes.d.ts, via next-env.d.ts; it contains zero imports (a pure string-literal route union), so it cannot produce the missing-module error. Source-only typecheck also passes with no .next present at all, so a fresh checkout is unaffected.

The new guard is scoped to the lint roots and to pushes touching TypeScript, skips loudly when node_modules is absent rather than pushing people to GUARD_PUSH_DISABLE=1 (which would lose the format and drift guards too), and is overridable with SKIP_STATIC_GUARD=1. Route-signature validation is not lost — next build still covers it in CI.

Verification

  • npm run verify:pr-localcould not complete, and not because of this diff: it fails closed at its check:installed-lock-parity precondition with playwright: installed 1.62.0 does not match locked 1.62.1, a pre-existing container drift. This diff changes no dependency. The gates it selects for this scope were run directly instead:
    • npm run lint — exit 0
    • npm run typecheck — exit 0 (base config, as CI runs it)
    • npm run test489 files passed, 5097 passed / 4 skipped
    • npm run build — compiled successfully; Client bundle secret surface check passed.
    • npm run check:bundle-budget1406.9 KiB gzip; baseline 1406.4 KiB; within tolerance
    • npx prettier --check .All matched files use Prettier code style! (repo-wide, not per-file)
    • npm run check:outstanding-issues235 rows (104 open, 131 archived), unique ids, next-id=238
    • npm run docs:check-scripts428 npm-run reference(s) resolve; docs:check-inventory current after docs:update
  • Guard-specific proof: node scripts/guard-push.mjs --self-test passes; tests/guard-push.test.ts15 passed; the guard was driven directly against reconstructions of both defects and blocked each with CI-identical messages (TS2339 ... Property 'devOnly' does not exist, error Calling setState synchronously within an effect).
  • Timing, so the hook is not something people disable: source typecheck is 54.7s cold / 6.9s warm incremental; lint is scoped to changed files.
  • npm run verify:ui — not run: no UI, routing, styling, or browser behaviour changed.
  • npm run verify:release — not run: no release or handoff confidence claimed.

Risk and rollout

  • Risk: the guard blocks a legitimate push. Bounded three ways — it only runs when lint-root or TypeScript files change, it fails open with a loud note when node_modules is absent, and SKIP_STATIC_GUARD=1 overrides it. The residual gap is deliberate: it reads the working tree rather than the pushed blobs (unlike the format guard, which materialises the commit because formatting drift between tree and commit is silent and common). A lint or type error present in the commit but not the tree implies editing after committing, which the guard surfaces with a dirty-tree note. Reproducing a node_modules-linked worktree for tsc costs more than that gap.
  • Rollback: revert. The guard is additive; tsconfig.typecheck.json and typecheck:source are new and unreferenced by CI, and no existing gate's behaviour changed.
  • Provider or production effects: None. No dependency, schema, runtime, or deployment change; every command run was local and offline.

Notes

#210 is closed with its residual stated rather than silently: plain npm run typecheck still reads .next and so still goes red against a stale build. That is correct behaviour for a stale artifact, and CI's Build job verifies the generated types for real — clear .next if you want the full check locally.

#237 is recorded but deliberately not decided here. The budget's totalGzipBytes comes from measureChunkPaths(walkJsFiles(CHUNKS_DIR)) — every built chunk — while the manifest-scoped initialDashboardChunks set is used only for the fixture-payload assertion. Choosing between "exclude mockup chunks so the number means production weight" and "keep counting them as a deliberate hygiene ceiling" changes what the gate is for, which is a call for the repo owner. Measured context: main sits at 1406.4 KiB against a 10% tolerance, and 59 of ~105 app routes are mockups.

No Clinical Governance Preflight section: this diff touches build tooling and documentation only — no ingestion, answer generation, search/ranking, source rendering, document access, privacy, or production env. No RAG impact: line for the same reason — no file under any RAG-ranking surface is touched.


Generated by Claude Code

Two open PRs burned full CI cycles this week on defects a single local
command would have caught: #1606 on a react-hooks/set-state-in-effect lint
error, #1618 on a TS2339 for `mode.devOnly` (a union member that lacks the
property, where app-modes.ts already exports the correct `"devOnly" in mode`
guard). Neither lint nor typecheck was in the pre-push path.
Typecheck could not simply be added, because it was already unusable
(outstanding-issues #210). tsconfig.json's `include` carries
`.next/types/**/*.ts` and `.next/dev/types/**/*.ts` — gitignored build
artifacts — so deleting a page leaves the stale generated validator importing
a removed module. Reproduced rather than inferred: a planted
`.next/dev/types/validator.ts` referencing a removed mockup page yields
`error TS2307: Cannot find module .../mockups/deleted-mockup-route/page.js`,
base config exit 2, source-only config exit 0. Full source typecheck is clean
(71s cold, 8.8s warm). Red locally and green in CI is how the gate got
abandoned, which is how the real type error then reached CI.
- tsconfig.typecheck.json + `typecheck:source`: identical compiler options,
minus the `.next` globs, with a separate tsbuildinfo so the two incremental
caches cannot invalidate each other. Route-signature validation is not lost;
`next build` still covers it in CI.
- guard-push.mjs gains a fourth guard running eslint over the pushed files and
this typecheck. Verified to reproduce both defects above with CI-identical
messages. Scoped to the lint roots and to pushes that touch TS, skips loudly
when node_modules is absent rather than pushing people to
GUARD_PUSH_DISABLE=1, and overridable with SKIP_STATIC_GUARD=1.
Also corrects a doc claim that made #1580 surprising: "mockups are exempt"
was being read as blanket. Mockups are exempt from the wiring and reachability
gates and nothing else — they are still typechecked, and their client chunks
still count toward check:bundle-budget, which totals every built chunk rather
than the initial production bundle. That the budget's scope contradicts
ledger #13's "not an initial production bundle" position is a real unmade
decision, now recorded as #237 rather than papered over.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T97Kqdj9Xh1Cubv5ms3KVy
@supabase

supabaseBot commented Aug 5, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:5 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f625643-50d1-47ab-a798-7e9f711193df

📥 Commits

Reviewing files that changed from the base of the PR and between ce3bd2d and 6af496b.

📒 Files selected for processing (16)
  • .githooks/pre-push
  • AGENTS.md
  • CLAUDE.md
  • docs/branch-review-ledger.md
  • docs/codebase-index.md
  • docs/outstanding-issues.md
  • docs/process-hardening.md
  • docs/scripts-index.md
  • package.json
  • scripts/guard-push.mjs
  • scripts/run-heavy.mjs
  • scripts/test-cache-path.mjs
  • tests/audit-remediation-tooling.test.ts
  • tests/guard-push.test.ts
  • tests/test-runner-safety.test.ts
  • tsconfig.typecheck.json

Comment @coderabbitai help to get the list of available commands.

#1606#1606 is closed, but it carried the one fix nothing else in the queue provides:
MobileResultFilterControl's native <select> paints a harsh system-blue highlight
on phones, and #1615 keeps that native select (its change is the iOS 16px
anti-zoom rule). So the fix does not survive #1615 landing.
Records it as #238 with the two defects the redo must not repeat: the unresolved
keyboard trap on disabled options, and the set-state-in-effect lint error that
PR #1620's new pre-push guard would now catch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T97Kqdj9Xh1Cubv5ms3KVy
claudeand others added 2 commits August 5, 2026 12:24
Keep main's #237#245 rows and both archive entries; regenerate
scripts-index inventory counts after the merge.
@BigSimmo
BigSimmo marked this pull request as ready for review August 5, 2026 15:20
devin-ai-integration[bot]

This comment was marked as resolved.

…umber
# Conflicts:
#	docs/outstanding-issues.md
#	docs/scripts-index.md
devin-ai-integration[bot]

This comment was marked as resolved.

cursoragentand others added 6 commits August 5, 2026 16:37
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Resolve outstanding-issues conflict after #1624 landed: keep main's
#249#251 fastest-wins rows, renumber this PR's four open items to
#252#255, and advance next-id to 256.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Treat typecheck:source:internal as a shared read-only coordinator lease with
a distinct per-worktree buildinfo file, drop the pinned in-repo cache path,
and harden staticGuard: acquire a short exclusive lease (fail-open when busy),
use a private eslint cache, escalate lint on eslint policy changes, fail closed
when the push tip is not HEAD, cover eslint-rules, and add Vitest coverage.
Align hook/docs wording with the fourth guard and point CLAUDE.md at #252.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Keep run-heavy routing (lint:changed:internal / typecheck:source:internal)
from the parallel babysit tip, and retain shared source-typecheck lease,
distinct per-worktree buildinfo, private eslint cache, eslint policy
escalation, HEAD≠tip fail-closed, Vitest coverage, and docs alignment.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Pre-push invokes run-heavy via plain node, so the npm_execpath spawn path
was skipped and the fallback dropped effectiveForwarded — undoing the
per-worktree buildinfo injection. Also warn when staticGuard passes on a
dirty working tree.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
@cursor

cursorBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Babysit update (head after Bugbot)

Addressed Devin review threads (all three resolved) and a Bugbot finding on this PR:

  1. Shared lease for typecheck:source:internal + distinct per-worktree --tsBuildInfoFile; dropped pinned in-repo buildinfo from tsconfig.typecheck.json.
  2. staticGuard goes through run-heavy (lint:changed:internal / lint:internal / typecheck:source:internal) with private eslint cache, short fail-open wait, eslint policy escalation, and HEAD≠tip fail-closed.
  3. CLAUDE.md points at #252 (post-issues: queue fastest-wins plan and capture three follow-ups #1624 renumber); hook/docs describe the fourth guard / SKIP_STATIC_GUARD=1.
  4. Bugbot fix:run-heavy fallback spawn (no npm_execpath, which is the pre-push path) now forwards effectiveForwarded so --tsBuildInfoFile is not dropped; dirty-tree success also warns.

Ledger rows for this PR’s open follow-ups are #252#255 (next-id=256). PR #1624 is already merged.

Local proof: guard-push --self-test passed; Vitest on guard-push / audit-remediation / test-runner-safety 57 passed; outstanding-issues guard green.

Keep docs:check-inventory green after adding the pre-push eslint wrapper
script to package.json.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

@BigSimmo
BigSimmo enabled auto-merge (squash) August 5, 2026 18:00
@BigSimmo
BigSimmo disabled auto-merge August 5, 2026 18:41
- Treat "Database focused-test capacity is full" as coordinator busy so
shared typecheck slot exhaustion fails open instead of faking a type error.
- Skip source typecheck when every changed .ts path is excluded by
tsconfig.typecheck.json (edge functions, archive, scratch, worktrees).
- Restore check-github-shell-access.mjs (and its Role notes) in the scripts index.
@BigSimmo
BigSimmo enabled auto-merge (squash) August 5, 2026 18:45
devin-ai-integration[bot]

This comment was marked as resolved.

…ache
Addresses follow-up Devin on PR #1620:
- Reorder staticGuard so tip-vs-HEAD fails closed only when lint/typecheck
will actually read the working tree; ignore tag refs in the tip check.
- Pin a distinct tsBuildInfoFile on tsconfig.typecheck.json so direct tsc
does not collide with the base config cache (run-heavy still overrides).
@cursor
cursorBot disabled auto-merge August 5, 2026 18:59
@cursor
cursorBot enabled auto-merge (squash) August 5, 2026 18:59
@BigSimmo
BigSimmo disabled auto-merge August 5, 2026 18:59
@cursor
cursorBot enabled auto-merge (squash) August 5, 2026 18:59
@BigSimmo
BigSimmo disabled auto-merge August 5, 2026 19:00
@BigSimmo
BigSimmo enabled auto-merge (squash) August 5, 2026 19:00
@BigSimmo
BigSimmo disabled auto-merge August 5, 2026 19:00
@BigSimmo
BigSimmo enabled auto-merge (squash) August 5, 2026 19:00
devin-ai-integration[bot]

This comment was marked as resolved.

@cursor
cursorBot disabled auto-merge August 5, 2026 19:10
Addresses Devin on PR #1620 — a prior eslint failure must still block the
push if the follow-up source typecheck cannot get a coordinator slot.
@cursor
cursorBot enabled auto-merge (squash) August 5, 2026 19:10
@cursor
cursorBot disabled auto-merge August 5, 2026 19:18
@cursor
cursorBot enabled auto-merge (squash) August 5, 2026 19:19
@cursor
cursorBot merged commit 9096d5a into mainAug 5, 2026
24 checks passed
@cursor
cursorBot deleted the claude/review-open-prs-fewxlh branch August 5, 2026 19:27
Sign up for freeto 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.

3 participants

@BigSimmo@claude@cursoragent