Cards specific to typescript. Use them with the level page that matches your input. Report each check as PASS, FAIL, or N/A with file:line evidence. A FAIL carries the card's severity. Any blockFAIL means request changes. A should-fix or discussFAIL goes in the review but does not block.
Verdict: request changes - change, function - 2 block, 1 should-fix FAIL, 1 PASS, 14 N/A
FAIL [block] chg-expand-contract - src/a.py:42 - renames the column and drops the old one in one migration
FAIL [block] svc-sqli-authn - src/db.py:88 - proven: builds the WHERE clause with an f-string from request.args
FAIL [should-fix] chg-one-concern - src/b.py:7 - a logging rewrite rides along with the bug fix
PASS chg-breaking-change-taxonomy - src/c.py:15 - no signature or return shape changes
N/A: 14 cards, applies_when does not hold
What the badges mean
blockmust be fixed before merge or release.
should-fixfix it, but it need not block.
discussraise it and decide together.
stablevetted; use for verdicts.
draftnot vetted; do not use for a verdict.
Tool gate: what a linter or CI check could enforce instead of a reviewer.
Draft cards
No stable cards yet.
5 draft cards
A draft card is not vetted. Report a FAIL of one under Draft findings, at should-fix at most, never in the ledger or the verdict. How a draft becomes stable
typescript-mock-at-the-boundary
should-fixdrafttestingmaintainability
Applies when: A Vitest test calls vi.mock on a module of the project's own code.
Rule. Fake the system boundary (network, clock, storage), for example with Mock Service Worker; do not vi.mock your own modules when a boundary fake would do.
Why. A mocked internal module makes the test check the mock. The test then passes while the real wiring is broken, and fails on a harmless refactor.
Counterpoint. "Some internal seams are slow or nondeterministic." Mock those at their edge (the clock, the random source), not the module that uses them.
Why it is a draft: too few organisations back a clause of the Rule. Vitest and Testing Library state it; Testing Library's own project counts once and a third is missing How a draft becomes stable
Applies when: TypeScript code calls an async function or creates a Promise.
Rule. Every Promise is awaited, returned, or given a .catch; enable @typescript-eslint/no-floating-promises.
Why. A Promise nobody handles turns a failure into an unhandled rejection, or into work that finishes after the code assumed it was done.
FAIL
async function onSave() {
saveDraft(doc); // returns a Promise; errors vanish
closeDialog();
}
PASS
async function onSave() {
await saveDraft(doc);
closeDialog();
}
Counterpoint. "Fire-and-forget is intended." Mark it with void saveDraft(doc).catch(report) so the choice is visible and failures are still reported.
Why it is a draft: it overlaps a stable card. The stable card fn-handle-rejected-promises covers unhandled rejections; merge or delete How a draft becomes stable
Applies when: A vi.mock factory replaces a module that exports error classes, enums or constants the code under test also uses.
Rule. A vi.mock factory spreads await importOriginal() and overrides only what the test needs; never swap an exported error class for a stub.
Why. Code under test does err instanceof ApiError. If the mock exports a different or empty class, the check is false and the error path is never exercised.
Counterpoint. "The real module has side effects on import." Then mock the effect's boundary; a factory that drops half the exports breaks other callers.
Why it is a draft: too few organisations back a clause of the Rule. Only the Vitest docs describe importOriginal; the instanceof failure is not documented by a second organisation How a draft becomes stable
Applies when: Several Vitest test files repeat the same setup (i18n init, MSW server, DOM matchers).
Rule. Put setup that every test file repeats into one file listed in setupFiles, not into each test file.
Why. Copies drift: one file inits i18n with other resources and a test passes alone but fails in the suite. One file is one place to change.
FAIL
// a.test.tsx, b.test.tsx, c.test.tsx each start with:
import i18n from 'i18next';
i18n.init({ lng: 'en', resources });
PASS
// vitest.config.ts
export default defineConfig({ test: { setupFiles: ['./test/setup.ts'] } });
// test/setup.ts runs i18n.init once per test file
Counterpoint. "A global setup hides what a test needs." Keep the shared file to things every test needs; per-test data stays in the test.
Why it is a draft: too few organisations back a clause of the Rule. Only the Vitest docs state it; needs two more organisations How a draft becomes stable
Rule.tsconfig.json sets "strict": true; switch off a single strict flag only with a written reason.
Why. The strict family (null checks, implicit any, strict function types) is where most type errors that reach users are caught. Without it, tsc passing says little.
Counterpoint. "Migrating a big codebase is too costly." Enable it per flag, or per package with project references, and ratchet; do not leave it off by default for new code.
Why it is a draft: too few organisations back a clause of the Rule. Only Microsoft's tsconfig reference states it; needs two more organisations How a draft becomes stable