MySQL2 — Pull Request Review Skill
The review checklist for MySQL2 pull requests. Read it before reviewing a pull request, a diff, or a branch.
Never approve a PR that violates an item below without first alerting the author.
General
- Tests: every bug fix and every new feature ships with tests, and the tests for a fix must fail without it.
- Documentation: every new feature is documented under
website/docs/.
- Node 14 compatibility: Node 14 is the minimum supported runtime, whatever the
engines field declares.
- Breaking changes: flag anything that can change existing behavior, even in a patch or a minor feature. The change itself is not an error, an unintentional semver violation is. A change to an existing test is the strongest signal there is, so read it closely for regressions.
- Comments: ask for a better implementation, never for a better explanation.
- An obvious comment is a finding on its own.
- Comment length measures the code underneath it. The more explanation it needs, the worse it usually is.
- A comment that explains the implementation is replaced by clear names, decoupled functions with a defined scope, and proper abstractions.
Tests
- Connection scope:
end(), close(), destroy(), and release() belong in a scope outside the assertions. The wrong shapes are not obvious, so check them against the section below.
process.exit: a conditional skip uses Poku's skip.
new Promise with setTimeout: waiting uses Poku's sleep.
node:assert and node:test: assertions and test structure come from poku, and strict replaces assert.
as unknown as and any: never in test files.
@ts-expect-error: only // @ts-expect-error: internal access or // @ts-expect-error: TODO: implement typings, and only when the type error is unrelated to the contribution. When it is related, the fix belongs in /typings.
- Timer-dependent tests: waiting on an internal timer is flaky in CI, so the test asserts the state synchronously or calls the internal method directly.
- Promise-based API: new tests prefer
.promise(). Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need both modes. A recommendation, not a rule.
async/await: describe, it, and test are awaited only when the callback is asynchronous.
Types
- Typings structure: types follow the existing structure in
/typings and never land in an arbitrary location. See the /types skill for the architecture and the known gaps.
Connection scope
The most frequent contributor mistake, and the most expensive: a failing assertion skips the teardown and the test process hangs until CI times out. Both wrong shapes below read as correct at a glance, so compare the diff against them directly.
// ❌ Wrong: end() sits in the same scope as the assertion
await describe('test', async () => {
await it('should do something', async () => {
const connection = await createConnection(); // same for pool or cluster connections
assert(false);
await connection.end(); // never reached
});
// process hangs
});
// ❌ Wrong: try-finally is a workaround, not a fix
await describe('test', async () => {
await it('should do something', async () => {
const connection = await createConnection();
try {
assert(false);
} finally {
await connection.end();
}
});
// process hangs
});
// ✅ Correct: end() in an outer scope
await describe('test', async () => {
const connection = await createConnection();
it('should do something', () => {
assert(false); // fails in its own scope
});
await connection.end(); // always reached
});
- Every teardown method and every connection type is affected:
close, end, destroy, release, on Connection, Pool, PoolCluster, and the rest.
- Each connection is isolated by a nested or dedicated
describe.
- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.
await conn.promise().end() replaces wrapping a callback in new Promise.
Verifying the branch
npm run lint # lint and formatting
npm run typecheck # type-check the project
npm test # full suite, or FILTER=path/to/test.mts npx poku for a single file
npm run test:build # build check
Integration and global tests need a running MySQL with a test database. The global suite runs sequentially and needs elevated privileges, and Poku skips those files when hasPrivileges() fails, so a green local run does not prove they were exercised.
1---2name: code-review3description: MySQL2 — Pull Request Review Skill4---5# MySQL2 — Pull Request Review Skill67The review checklist for MySQL2 pull requests. Read it before reviewing a pull request, a diff, or a branch.89Never approve a PR that violates an item below without first alerting the author.1011## General12131. **Tests:** every bug fix and every new feature ships with tests, and the tests for a fix must fail without it.142. **Documentation:** every new feature is documented under `website/docs/`.153. **Node 14 compatibility:** Node 14 is the minimum supported runtime, whatever the `engines` field declares.164. **Breaking changes:** flag anything that can change existing behavior, even in a patch or a minor feature. The change itself is not an error, an unintentional semver violation is. A change to an existing test is the strongest signal there is, so read it closely for regressions.175. **Comments:** ask for a better implementation, never for a better explanation.18 - An obvious comment is a finding on its own.19 - Comment length measures the code underneath it. The more explanation it needs, the worse it usually is.20 - A comment that explains the implementation is replaced by clear names, decoupled functions with a defined scope, and proper abstractions.2122## Tests23246. **Connection scope:** `end()`, `close()`, `destroy()`, and `release()` belong in a scope outside the assertions. The wrong shapes are not obvious, so check them against the section below.257. **`process.exit`:** a conditional skip uses Poku's `skip`.268. **`new Promise` with `setTimeout`:** waiting uses Poku's `sleep`.279. **`node:assert` and `node:test`:** assertions and test structure come from `poku`, and `strict` replaces `assert`.2810. **`as unknown as` and `any`:** never in test files.2911. **`@ts-expect-error`:** only `// @ts-expect-error: internal access` or `// @ts-expect-error: TODO: implement typings`, and only when the type error is unrelated to the contribution. When it is related, the fix belongs in `/typings`.3012. **Timer-dependent tests:** waiting on an internal timer is flaky in CI, so the test asserts the state synchronously or calls the internal method directly.3113. **Promise-based API:** new tests prefer `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need both modes. A recommendation, not a rule.3214. **`async`/`await`:** `describe`, `it`, and `test` are awaited only when the callback is asynchronous.3334## Types353615. **Typings structure:** types follow the existing structure in `/typings` and never land in an arbitrary location. See the [`/types` skill](../../../.claude/skills/types/SKILL.md) for the architecture and the known gaps.3738## Connection scope3940The most frequent contributor mistake, and the most expensive: a failing assertion skips the teardown and the test process hangs until CI times out. Both wrong shapes below read as correct at a glance, so compare the diff against them directly.4142```ts43// ❌ Wrong: end() sits in the same scope as the assertion44await describe('test', async () => {45 await it('should do something', async () => {46 const connection = await createConnection(); // same for pool or cluster connections47 assert(false);48 await connection.end(); // never reached49 });50 // process hangs51});5253// ❌ Wrong: try-finally is a workaround, not a fix54await describe('test', async () => {55 await it('should do something', async () => {56 const connection = await createConnection();57 try {58 assert(false);59 } finally {60 await connection.end();61 }62 });63 // process hangs64});6566// ✅ Correct: end() in an outer scope67await describe('test', async () => {68 const connection = await createConnection();6970 it('should do something', () => {71 assert(false); // fails in its own scope72 });7374 await connection.end(); // always reached75});76```7778- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.79- Each connection is isolated by a nested or dedicated `describe`.80- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.81- `await conn.promise().end()` replaces wrapping a callback in `new Promise`.8283## Verifying the branch8485```sh86npm run lint # lint and formatting87npm run typecheck # type-check the project88npm test # full suite, or FILTER=path/to/test.mts npx poku for a single file89npm run test:build # build check90```9192Integration and global tests need a running MySQL with a `test` database. The global suite runs sequentially and needs elevated privileges, and Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.