| name | pr-quality-gate |
| description | Runs automatically before creating a PR. Validates test coverage for new code, generates a coverage diff against the base branch, and ensures the internal testing framework is used. |
PR Quality Gate
This skill enforces quality standards before a PR is created. It MUST be applied automatically whenever the agent is about to create a pull request (e.g. via gh pr create or the /commit + PR flow).
When to activate
Run this checklist before calling gh pr create. Do NOT skip any steps.
Step 1: Verify new code has tests
- Run
git diff <base-branch>...HEAD --name-only to list all changed files.
- For each new or modified
.ts source file in packages/pipes/src/ (excluding *.test.ts, index.ts, and types.ts):
- Check if a corresponding
.test.ts file exists next to it.
- If the file contains new exported functions, classes, or significant logic changes — there MUST be tests covering them.
- If tests are missing, write them before proceeding or flag it explicitly in the PR description under a
## Missing tests section.
Step 2: Use the internal testing framework
When writing tests for this project, always prefer the built-in test utilities over creating ad-hoc mocks:
EVM tests
import { encodeEvent, evmPortalMockStream, mockBlock, resetMockBlockCounter } from '~/testing/evm/index.js'
import { createMockPortal, readAll } from '~/testing/index.js'
mockBlock() — creates a block with auto-generated metadata (number, hash, parentHash, timestamp)
encodeEvent() — encodes event args into portal log format using a viem ABI
evmPortalMockStream() — wraps blocks into a mock HTTP portal server
createMockPortal() — lower-level mock portal for custom response sequences (204, 409, 503, etc.)
readAll() — consumes a portal stream into an array
resetMockBlockCounter() — call in beforeEach for deterministic block numbers
General tests
import { createTestLogger } from '~/testing/index.js'
import { MockPortal, createMockPortal, MockResponse } from '~/testing/index.js'
Test conventions
- Tests live next to source:
feature.ts → feature.test.ts
- Use
describe / it / beforeEach / afterEach from vitest
- Clean up resources (mock portals, connections) in
afterEach, not try/finally
- Use
toEqual for deep comparison, toMatchInlineSnapshot for complex output
- Run tests from the package directory:
pnpm vitest run src/path/to/file.test.ts
Step 3: Start test infrastructure via Docker Compose
Integration tests require ClickHouse and PostgreSQL. Start them using the project's docker-compose.yml:
cd packages/pipes
docker compose up -d
This starts:
- ClickHouse on port
10123 (image: clickhouse/clickhouse-server:25.10)
- PostgreSQL on port
5432 (image: postgres:18, user: postgres, password: postgres)
Environment variables (defaults already match the compose config):
TEST_CLICKHOUSE_URL=http://localhost:10123
TEST_POSTGRES_DSN=postgresql://postgres:postgres@localhost:5432/postgres
Wait for services to be healthy before running tests. Do NOT tear down containers after — leave them running for subsequent test runs.
Step 4: Generate coverage diff
Run coverage on both the base branch and the current branch, then include the diff in the PR body.
Procedure
CURRENT_BRANCH=$(git branch --show-current)
BASE_BRANCH="design/sdk1"
cd packages/pipes
pnpm vitest run --coverage --coverage.reporter=json-summary --bail=0 2>/dev/null
cp coverage/coverage-summary.json /tmp/coverage-head.json
cd ../..
git stash
git checkout $BASE_BRANCH
cd packages/pipes
pnpm vitest run --coverage --coverage.reporter=json-summary --bail=0 2>/dev/null
cp coverage/coverage-summary.json /tmp/coverage-base.json
cd ../..
git checkout $CURRENT_BRANCH
git stash pop 2>/dev/null
Parse and format the diff
Read both JSON files. Group individual file entries by their parent directory (e.g. src/core/query-builder.ts → src/core). For each directory, sum covered and total across all files to compute the directory-level percentage. Format as a markdown table showing only the total row and directories where coverage changed (|Δ| >= 0.1):
## Coverage
| Module | Stmts | Δ | Branch | Δ |
|--------|-------|---|--------|---|
| **All files** | 75.1% | +0.3 | 80.6% | +1.0 |
| src/core | 73.2% | +1.1 | 85.8% | +2.4 |
| src/internal | 92.9% | +1.8 | 85.5% | +7.4 |
Rules:
- Group by directory, NOT individual files
- Show Δ with
+ / - prefix
- Bold any module where statements dropped by more than 1%
- Only show directories with changes (|Δ| >= 0.1) — skip unchanged modules
- If overall coverage decreased, add a warning:
⚠️ Overall coverage decreased
Include in PR body
Append the coverage table to the PR description body before calling gh pr create.
Step 5: PR description format
Every PR created by the agent must follow this template:
## Summary
<1-3 bullet points describing what changed>
## Coverage
<coverage diff table from Step 4>
## Test plan
<bulleted checklist of what is tested>
🤖 Generated with [Claude Code](https://claude.com/claude-code)