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-onlyto list all changed files. - For each new or modified
.tssource file inpackages/pipes/src/(excluding*.test.ts,index.ts, andtypes.ts):- Check if a corresponding
.test.tsfile exists next to it. - If the file contains new exported functions, classes, or significant logic changes — there MUST be tests covering them.
- Check if a corresponding
- If tests are missing, write them before proceeding or flag it explicitly in the PR description under a
## Missing testssection.
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 ABIevmPortalMockStream()— wraps blocks into a mock HTTP portal servercreateMockPortal()— lower-level mock portal for custom response sequences (204, 409, 503, etc.)readAll()— consumes a portal stream into an arrayresetMockBlockCounter()— call inbeforeEachfor 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/afterEachfrom vitest - Clean up resources (mock portals, connections) in
afterEach, nottry/finally - Use
toEqualfor deep comparison,toMatchInlineSnapshotfor 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:10123TEST_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
# 1. Record current branch
CURRENT_BRANCH=$(git branch --show-current)
BASE_BRANCH="design/sdk1" # or the PR target branch
# 2. Run coverage on current branch (JSON summary)
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
# 3. Stash changes, switch to base, run coverage
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
# 4. Return to working branch
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)