Skip to content

Cut CI to a two-job gate and weekly E2E and AI evals - #283

Merged
Programcode merged 2 commits into
mainfrom
t3/slim-github-actions-ci
Oct 30, 2026
Merged

Programcode merged 2 commits into
mainfrom
t3/slim-github-actions-ci

Conversation

@Programcode

@Programcode Programcode commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Cuts GitHub Actions from about 4,400 to about 1,500 billed minutes a month. The pull-request gate drops from five jobs to two and from about 2.5 minutes to 1.6 minutes of wall time (2.2 minutes with a cold cache). CI no longer runs unit tests. The end-to-end suites and the AI evals still run, weekly or on demand, instead of daily.

What changed

Quality (deploy.yml). The gate is now two jobs:

  • check (pull requests and pushes to main) runs docs, lint, typecheck, the guards, Edge Function tests and build. The build step catches a broken build before Vercel does.
  • browser-smoke (pull requests only) is unchanged.

The lint, unit, edge and typecheck-build jobs are gone. GitHub rounds each job up to a whole minute, so four short jobs cost four minutes even though the work fits in about two.

Unit tests. I deleted 89 Vitest files, plus their setup, helpers and mocks, and uninstalled jsdom, @testing-library/* and @vitest/coverage-v8. Most were component, page, store and hook tests. The set also included tests you may want to veto one by one:

  • the stream and partial-JSON parsers (src/test/ai/streaming.test.ts, src/lib/ai/partial-json.test.ts)
  • client auth (session-expiry, auth-callback, AuthProvider.behavior) and checkout return (checkout-return)
  • the Crossfire reducer and Sparring format rules
  • two script tests, backend-status.test.ts and db-gate.test.ts (the second landed in Make local Supabase verification parallel-safe with two slots #280 this morning)

Every file is recoverable with git show e237d9f2:<path>. Deleting them beats leaving them unrun: tests nobody runs rot and still mislead agents. The collaboration service keeps its own tests (npm run test:collaboration, 30 tests, 3 s). The service is hidden, so neither CI nor verify runs them. Vitest now runs 12 guard files, listed explicitly in vitest.config.ts, in node in about 2 seconds. Each one reads repo config or content and checks a rule no browser run can see:

  • Claude-only model routing (cost)
  • Vercel security headers and which branches deploy
  • server-owned quota, plus Stripe customer and redirect ownership
  • the staging sync and e2e refusing to target the production project
  • i18n catalog parity
  • the SEO content rules

Kept on purpose: Edge Function tests (Deno, about 25 s inside check). These are unit-level tests too. I kept them because Edge Functions own auth, quota, billing and cost accounting, and no pull-request browser test reaches a real Edge Function. They run inside check, so they add no billed job. Say the word and they go.

Schedules. Daily suites become weekly, on staggered days so no two start local Supabase the same day. Today's Database run failed with toomanyrequests: Data limit exceeded from public.ecr.aws.

  • AI evals (live-e2e.yml): Mondays. The model matrix is now a second job in this workflow (model-matrix.yml is deleted). Dispatch with -f suite=live|model-matrix|both.
  • Browser suite: Tuesdays. The file is renamed from browser-nightly.yml to browser-weekly.yml.
  • Agentic suite: Wednesdays. The bug bash moves from weekly to the 1st of each month.
  • Full database gate: Thursdays. Pull-request pgTAP on schema changes is unchanged.
  • Protected staging E2E: daily at 07:11 UTC and on dispatch from main, instead of after every push to main. About half of each old run's billed time went to polling for the staging sync. Before a backend production deploy, dispatch it (docs/operations.md now says so). The VPS sync's hold-off matches on workflow file and job name, so a scheduled run still holds the sync.
  • Production canary: unchanged. It runs after every push to main and daily, and the daily run makes two real AI requests, so provider drift still shows within a day.

Instructions. AGENTS.md, docs/testing.md, docs/operations.md (CI section rewritten), docs/development.md, docs/staging.md, docs/standards/security.md, README and three verify-debatelab feature notes now ask for end-to-end or backend tests (Playwright, agentic, Deno, pgTAP). None of them asks for unit tests anymore. npm run verify mirrors the new gate.

Before and after

Assumptions: about 150 pull-request runs, 50 pushes to main and 15 schema pull requests a month, and 4.33 weeks a month. Durations come from today's runs unless marked est. Billed minutes round each job up to a whole minute.

Workflow Trigger before Trigger after Wall per run Billed/run Billed/month before Billed/month after
Quality, pull request every PR, 5 jobs every PR, 2 jobs 2.5 min → 1.6 min (2.2 cold) 6 → 4 (5 cold) 900 600
Quality, push to main every push, 4 jobs every push, 1 job 1.4 min → ~1.5 min 4 → 2 200 100
Database pgTAP schema PRs schema PRs 1.7 min 2 30 30
Database full gate daily Thursdays ~20 min (est, docs) 20 600 87
Browser suite daily Tuesdays 11 to 15 min 12 360 52
Live provider journeys daily Mondays (AI evals) 7.7 min 8 240 35
Model matrix Mondays Mondays (AI evals) ~10 min (est) 10 43 43
Agentic suite daily, 4 legs Wednesdays, 4 legs 13 to 14 min 41 1,230 178
Agentic bug bash Sundays, 4 legs 1st of month ~15 min (est) 61 264 61
Production canary push + daily push + daily 1.4 to 2 min 2 to 3 190 190
Protected staging E2E after every main push daily + dispatch 4.4 min → ~1.5 min 6 → ~3 300 90
Staging preview note supabase PRs opened unchanged 7 s 1 5 5
Total ~4,360 ~1,470

October's real spend was higher. Live, model-matrix and agentic still ran on pull requests then, and the canary ran on every deployment_status. Those triggers were already gone from main, so the "before" column uses main as it stood, not October's history. Model matrix, full gate and bug bash have no successful run on record (Actions billing failed most October runs), so their durations are estimates.

Verification

  • npm run verify at the final head: all 9 steps passed in 96.5 s (lint 36.6 s, build 35.1 s, smoke 29.7 s, typecheck 25.6 s).
  • actionlint 1.7.7 on every workflow: clean.
  • The 12 guard files pass under node (94 tests, 1.9 s).
  • CI on this PR: the first run (8015a0b) was cold, since the lockfile changed and npm ci ran: check 128 s, browser-smoke 115 s, 2.2 min of wall time, 5 billed minutes. The run on b931cb1 was warm: check 91 s, browser-smoke 69 s, 1.6 min of wall time, 4 billed minutes. pgTAP runs because database.yml changed. Its first run failed on public.ecr.aws toomanyrequests: Data limit exceeded while pulling the Postgres image, which is infrastructure, not this diff. On b931cb1 it passed, so all 5 checks are green at the final head.
  • Reviews: Codex r1 on 8015a0b found one medium issue: scheduled staging E2E failed when Quality was still running. Fixed in b931cb1 by polling. Codex r2 on b931cb1 found nothing. Copilot (quota) and CodeRabbit (116 files, over its 100-file limit) skipped the PR.

Not verified: the new schedules and the live-e2e.yml dispatch matrix only run on main or by dispatch, so no run here exercised them. actionlint checked the matrix expression's types. The first scheduled runs are Monday's AI evals and Tuesday's browser suite.

Already red before this PR: today's daily production canary failed at canary.spec.ts:97 (streamed Ask), which t3/fix-canary-ask-model-picker is fixing.

Merge rule

This PR is not low-risk under AGENTS.md: it changes .github/, package.json and package-lock.json. It needs Adam's OK to merge.

🤖 Generated with Claude Code

Quality now runs one `check` job (docs, lint, types, guards, Edge Function
tests, build) beside the `@smoke` browser job. The unit-test job is gone:
89 component, store and page tests are deleted, and Vitest keeps 12 guard
files that read repo config and content (Claude-only routing, Vercel
headers, production-target refusals, quota and Stripe ownership, i18n
parity, SEO rules).

The browser suite, full database gate, agentic suite and AI evals move
from daily to weekly on staggered days. The model matrix becomes a job of
live-e2e.yml, the bug bash runs monthly, and protected staging runs daily
and on demand instead of after every push. Prod canary is unchanged.

AGENTS.md and the docs now ask for end-to-end or backend tests, not unit
tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 30, 2026 16:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 116 files, which is 16 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between e237d9f and b931cb1.

Files ignored due to path filters: 1
  • package-lock.json is excluded by !**/package-lock.json
Files selected for processing: 116
  • .claude/skills/verify-debatelab/features/ai-failures.md
  • .claude/skills/verify-debatelab/features/auth-demo-login.md
  • .claude/skills/verify-debatelab/features/voice-rounds.md
  • .github/workflows/agentic-e2e.yml
  • .github/workflows/browser-weekly.yml
  • .github/workflows/database.yml
  • .github/workflows/deploy.yml
  • .github/workflows/live-e2e.yml
  • .github/workflows/model-matrix.yml
  • .github/workflows/staging-e2e.yml
  • AGENTS.md
  • README.md
  • docs/development.md
  • docs/operations.md
  • docs/staging.md
  • docs/standards/security.md
  • docs/testing.md
  • e2e/authed-pages.spec.ts
  • package.json
  • scripts/verify.mjs
  • src/components/layout/phone-tab-bar.test.ts
  • src/hooks/use-external-redirect-pending.test.tsx
  • src/hooks/use-subscription.test.tsx
  • src/lib/ai/errors.test.ts
  • src/lib/ai/failure-reason.test.ts
  • src/lib/ai/features/session-quota.test.ts
  • src/lib/ai/limit-message.test.ts
  • src/lib/ai/partial-json.test.ts
  • src/lib/ai/request-failure.test.ts
  • src/lib/ai/run-recovery.test.ts
  • src/lib/ai/sources.test.ts
  • src/test/a11y/names-and-headings.test.tsx
  • src/test/ai/anthropic-cache-breakpoints.test.ts
  • src/test/ai/live.test.ts
  • src/test/ai/sparring-live.test.ts
  • src/test/ai/streaming.test.ts
  • src/test/auth/AuthProvider.behavior.test.tsx
  • src/test/auth/account-switch-route.test.tsx
  • src/test/auth/auth-callback-page.test.tsx
  • src/test/auth/auth-callback.test.ts
  • src/test/auth/login-form.test.tsx
  • src/test/auth/login-google-redirect.test.tsx
  • src/test/auth/login-otp.test.tsx
  • src/test/auth/onboarding-per-account.test.ts
  • src/test/auth/session-expiry.test.ts
  • src/test/auth/sign-out-drafts.test.ts
  • src/test/billing/checkout-return.test.ts
  • src/test/billing/pricing-modal-redirect.test.tsx
  • src/test/components/EnvironmentSwitcher.test.tsx
  • src/test/components/LimitReachedModal.test.tsx
  • src/test/components/Markdown.test.tsx
  • src/test/components/SubscriptionStatus.test.tsx
  • src/test/components/WebTelemetry.test.tsx
  • src/test/crossfire/crossfire-daily-limit.test.tsx
  • src/test/crossfire/reducer.test.ts
  • src/test/date-format.test.ts
  • src/test/debate/debate-context.test.tsx
  • src/test/debate/debate-gates.test.tsx
  • src/test/docs/check-docs.test.ts
  • src/test/free-tools/motion-bank.test.ts
  • src/test/free-tools/round-timer.test.ts
  • src/test/free-tools/speech-time.test.ts
  • src/test/generate/generate-form-role.test.tsx
  • src/test/helpers/cases.ts
  • src/test/helpers/debate.ts
  • src/test/helpers/page-lifecycle.ts
  • src/test/i18n/localized-errors.test.tsx
  • src/test/i18n/url-language.test.tsx
  • src/test/jam-loader.test.ts
  • src/test/lib/activation-events.test.ts
  • src/test/lib/case-exports.test.ts
  • src/test/lib/case-view.test.ts
  • src/test/lib/flowchart.test.ts
  • src/test/lib/lazy-chunk.test.ts
  • src/test/lib/reload-stale-chunk.test.ts
  • src/test/mocks/supabase.ts
  • src/test/onboarding/debateowl-intro.test.tsx
  • src/test/onboarding/tour-chunk-fallback.test.tsx
  • src/test/pages/AskAccountSwitch.test.tsx
  • src/test/pages/AskStreamFallback.test.tsx
  • src/test/pages/CrossfireVoice.test.tsx
  • src/test/pages/FlowchartPage.test.tsx
  • src/test/pages/GeneratePage.test.tsx
  • src/test/pages/SharedPage.test.tsx
  • src/test/pages/SparringVoice.test.tsx
  • src/test/scripts/backend-status.test.ts
  • src/test/scripts/db-gate.test.ts
  • src/test/seo/best-ai-for-debate.test.ts
  • src/test/seo/brand-entity.test.ts
  • src/test/seo/page-head.test.tsx
  • src/test/service-worker-embed.test.ts
  • src/test/settings/settings-migration.test.ts
  • src/test/setup.ts
  • src/test/source-import.test.ts
  • src/test/sparring/formats.test.ts
  • src/test/sparring/sparring-quota-start.test.tsx
  • src/test/sparring/sparring-round.test.tsx
  • src/test/stores/account-local-state.test.ts
  • src/test/stores/delete-saved-case.test.ts
  • src/test/stores/library-sources.test.ts
  • src/test/stores/load-saved-case.test.ts
  • src/test/stores/resilient-local-storage.test.ts
  • src/test/stores/session-save-auth.test.ts
  • src/test/supabase/local-preview-env.test.ts
  • src/test/supabase/offline-reads.test.ts
  • src/test/timer/timer-clock.test.ts
  • src/test/timer/timer-shortcuts.test.ts
  • src/test/tools/tool-run.test.tsx
  • src/test/voice/delivery.test.ts
  • src/test/voice/opponent-voice.test.ts
  • src/test/voice/use-speech-capture.test.tsx
  • src/test/workspace/artifact-save-queue.test.ts
  • src/test/workspace/durable-ack.test.ts
  • src/test/workspace/invite-handoff.test.ts
  • tsconfig.app.json
  • vitest.config.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

A commit merged just before the daily schedule can reach staging while its
Quality push run is still queued. The step now polls for up to 10 minutes
instead of failing on the first look.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Programcode

Copy link
Copy Markdown
Owner Author

Codex review r1 (head 8015a0b) had one finding. Medium: on the scheduled path, staging E2E checked Quality's result once and would fail while Quality on main was still queued or running. Fixed in b931cb1: the step now polls for up to 10 minutes. I tested the query against a finished main commit and against a SHA with no run.

The pgTAP failure on 8015a0b was public.ecr.aws returning toomanyrequests: Data limit exceeded while pulling the Postgres image. That's infrastructure, not code, and this PR changes no schema. I reran that job once.

@Programcode
Programcode merged commit fec6cf0 into main Oct 30, 2026
5 checks passed
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