test-discipline
Skill3 starsChanged 8 days agoMITView on GitHub
Review test changes for the patterns that hollow out a test suite over time — overly-mocked tests, snapshot churn, asserting on the implementation, deleted tests without replacement, skipped tests left in the diff, and "fix the test to match the new behavior" without thinking about whether the old assertion was load-bearing. Apply to any PR that adds, deletes, or modifies test files.
- Reads credentials
15 other projects use a version of this.
Adopted by
Most recently taken up by
- niksavis/handilytheir own version, first seen Nov 8, 2026
- github/squadtheir own version, first seen Nov 8, 2026
- niksavis/basiclytheir own version, first seen Nov 7, 2026
- Dimon94/skillstheir own version, first seen Nov 1, 2026
- Dimon94/dveritytheir own version, first seen Nov 1, 2026
- microsoft/fsi-banking-payments-dispute-resolution-acceleratortheir own version, first seen Oct 25, 2026
Used with
Projects with it also use squad-commands, architectural-proposals and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
Microsoft AzureSkillsquad-commands
Categorized catalog of common Squad operations. Coordinator reads this file and presents it as an interactive menu when the user asks for available commands or help.
Also in 4 projects that use this
Microsoft AzureSkillarchitectural-proposals
How to write comprehensive architectural proposals that drive alignment before code is written
Also in 3 projects that use this
- +10 more from Azure
Other versions15 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 15
- github/gh-aw★ 5.6ksame name
- github/squad★ 3.4ksame name
- microsoft/waza★ 1.5ksame name
- Azure/fleet★ 227same name
- kubefleet-dev/kubefleet★ 164same name
- Dimon94/dverity★ 107same name
- Dimon94/skills★ 107same name
- microsoft/devsquad-copilot★ 50same name
- Azure/apiops-cli★ 32same name
- microsoft/github-vteam-industry-demos★ 2same name
5 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository66 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- clud-bug-collaborationSkill
- critical-issues-onlySkill
- evidence-based-reviewSkill
- respect-existing-conventionsSkill
- skill-frontmatter-qualitySkill
- apca-contrastSkill
- api-contract-enforcementSkill
- brand-voice-reviewSkill
- chroma-harmonizationSkill
- clud-bug-collaborationSkill
- component-sizing-principlesSkill
- component-sizingSkill
- composing-a-screenSkill
- consuming-a-design-systemSkill
- conventions-templateSkill
- critical-issues-onlySkill
- curating-a-skill-catalogSkill
- designing-a-design-systemSkill
- designing-elite-uiSkill
- design-system-consistencySkill
- design-token-namingSkill
- dtcg-formatSkill
- empirical-design-principlesSkill
- evidence-based-reviewSkill
- finding-a-catalog-skillSkill
- frontend-a11ySkill
- guarding-a-regressionSkill
- ios-safari-surfacesSkill
- line-height-gridSkill
- logmindSkill
- motion-continuitySkill
- oklch-color-spaceSkill
- orchestrating-agent-delegationSkill
- orchestrating-a-multi-agent-runSkill
- orchestrating-elite-agent-qaSkill
- palette-relationshipsSkill
- pii-and-complianceSkill
- proving-an-absenceSkill
- respect-existing-conventionsSkill
- retiring-a-superseded-decisionSkill
- reviewing-design-workSkill
- semver-design-tokensSkill
- session-heartbeatSkill
- skillforgeSkill
- skill-frontmatter-qualitySkill
- spacing-systemSkill
- technical-writingSkill
- thrill-designSkill
- token-frugal-toolingSkill
- token-naming-conventionsSkill
- type-scaleSkill
- udts-component-sizing-laddersSkill
- udts-dtcg-extensionsSkill
- udts-linter-rulesSkill
- udts-naming-conventionSkill
- udts-reviewSkill
- udts-semver-defaultsSkill
- udts-spacing-defaultsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
4 rules. 0 are common, 2 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 13 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 165
Before flagging, scan the rest of the suite for context:
- Very specific: line 167
If the codebase already has integration tests covering the path your skill is worried about, downgrade unit-level deletions ("integration …
- Distinctive: line 168
If the codebase uses snapshot tests pervasively (it's the established convention), don't flag every snapshot — only flag snapshots on …
- Very specific: line 169
If the codebase has a documented testing strategy (TESTING.md, docs/testing.md), defer to it.
“Common” is measured: kept by at least 6 in 10 of the 13 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Test discipline
Tests don't decay because anyone wrote a bad test on purpose. They decay because a hundred small "make the test pass" edits accumulate, each one defensible in isolation, until the suite no longer catches the bugs it was written to catch.
This skill flags the categories of test edit that quietly hollow out the suite. The bar is not "this test is bad" — it's "this test edit makes the suite less useful than it was before this PR."
Apply this skill any time a diff touches *.test.*, *_test.*, __tests__/, tests/, spec/, test/, or any other test file or fixture.
Categories to flag
1. Deleted assertions without replacement
it('charges the customer card', async () => {
const order = await placeOrder({ ... })
expect(order.status).toBe('paid')
- expect(charge.amount).toBe(2499)
- expect(charge.currency).toBe('usd')
})The remaining assertion (status === 'paid') doesn't catch the regression class the deleted ones were guarding (wrong amount, wrong currency). If the deleted assertions are obsolete because the behavior changed, the replacement assertions need to live somewhere — same test, different test, integration test. Flag and ask: what's the new check that protects this code path?
2. Tests deleted entirely, no replacement in diff
- describe('checkout flow', () => {
- it('rejects expired cards', () => { ... })
- it('handles 3DS challenge', () => { ... })
- })Sometimes deletion is right — the code under test was removed, the test was a duplicate, the test was flaky and unfixable. But the PR description should say which case it is. If the description just says "remove old tests," that's the smell. Flag and ask for the reason.
3. Tests rewritten to match new (buggy?) behavior
- expect(parseAmount('1,234.56')).toBe(1234.56)
+ expect(parseAmount('1,234.56')).toBe(123456)The assertion changed in the same PR that changed the implementation. Either the old assertion was wrong (in which case there should be a separate "bug found, here's the fix" commit and the regression is documented), or the new code is wrong and the test was edited to make it pass. Flag and ask: was the old behavior correct, and what changed in the spec?
4. Mocks that hide what the test should verify
+ jest.mock('./billing', () => ({ chargeCard: jest.fn().mockResolvedValue({ ok: true }) }))
+ it('completes checkout', async () => {
+ await completeCheckout({ ... })
+ expect(navigate).toHaveBeenCalledWith('/thank-you')
+ })The test passes if checkout calls billing with literally any arguments — wrong amount, wrong customer, wrong card. The mock returns { ok: true } regardless. This test now catches navigation regressions but no longer catches billing regressions. Either the mock is doing too much (replace with a stub that records args + assert on args), or the test is in the wrong layer (move to a unit test of the navigation logic alone).
5. Snapshot tests on rapidly-changing output
+ expect(renderInvoice(order)).toMatchSnapshot()Snapshots are a smell when the output they capture has many fields and a high churn rate. Six months from now the snapshot file will have been "regenerated to match new behavior" twenty times, with no human review of what actually changed. Prefer assertions on the specific fields the test was written to protect. Snapshots are fine for stable, dense output (parser ASTs, deterministic serialization) — flag them on UI output, JSON payloads, log lines.
6. .skip / .only / xit left in the diff
+ it.skip('handles the refund case', () => { ... })
+ describe.only('checkout', () => { ... }).skip is a TODO marker that won't run. .only makes CI silently skip every other test. Both ship green CI and break the suite's coverage. Flag every occurrence — the question is "is this intentional, and is there a tracking issue?" Acceptable answers: yes + linked issue. Unacceptable: "oops, debug leftover."
7. if (process.env.CI) skip gates
+ it('integration test', async () => {
+ if (!process.env.RUN_INTEGRATION) return
+ ...Tests that skip themselves based on env var run in some environments but not others, and the silent-skip path means CI looks green when the test never executed. Either the test runs everywhere, or it lives in a separate suite that's explicitly named (integration.test.ts). Flag the in-test skip.
8. Tests for code paths that don't exist yet
+ it('handles WebAssembly modules', () => {
+ // TODO: implement when wasm support lands
+ expect(true).toBe(true)
+ })A green test that asserts nothing is worse than no test — it gives false confidence. Either delete it or implement the assertion.
9. Time-dependent assertions without freezing time
+ expect(createTimestamp()).toBe(new Date().toISOString())This passes on the local machine in the same millisecond and fails in CI. The fix: freeze time with jest.useFakeTimers() / sinon.useFakeTimers() / equivalent, then assert on the frozen value. Flagged for the flakiness it creates.
10. Asserting on internal state, not observable behavior
+ expect(component.state.isOpen).toBe(true)
+ expect(internalCache.size).toBe(3)These assertions break when you refactor the component to use a different state shape — even though the externally-visible behavior is unchanged. Either move the assertion to the public surface (what does the user see when isOpen is true?), or accept the coupling and document it. Flag with a question: can we assert on the rendered output / API response instead?
Categories that are NOT smells
To stay calibrated, here's what looks like a problem but usually isn't:
- A test rewriting itself to use a new API as the production code's API changed — that's correct. Production API change + test rewrite move together. The smell is the test rewriting to a different assertion than the old one.
- A test moved to a different file — fine if the assertion is preserved. Smell if the assertion is silently lost in the move.
- A small number of
expects per test (1-3) — that's good; tightly scoped tests are easier to diagnose than mega-tests. - Tests for newly-added code paths landing alongside the code — that's what TDD looks like, regardless of whether the test was written before or after.
Finding-shape template
[test-discipline]
<category>:<file>:<line><the offending diff>Regression class this no longer catches:
<what specific bug could now slip through>Suggested fix: <one of: keep the old assertion, move it to a different layer, replace the mock with a stub-with-asserts, link a tracking issue for the
.skip>
The most useful framing is what bug could slip through now. That gives the author a concrete reason to push back ("we have an integration test that catches this case") or a concrete fix ("you're right, I'll keep the amount assertion").
When this skill should be silent
- The PR doesn't touch any test files or fixtures.
- The test changes are purely mechanical (rename, import path update, formatting) with no assertion changes, no skips, no mocks added.
- The PR is removing tests AND removing the production code those tests covered (clean removal of a feature; the test is correctly going with it).
In any of those cases:
[test-discipline] n/a — no behavioral test changes in this diff.
Working with the existing suite
Before flagging, scan the rest of the suite for context:
- If the codebase already has integration tests covering the path your skill is worried about, downgrade unit-level deletions ("integration test at
<path>still covers this regression class"). - If the codebase uses snapshot tests pervasively (it's the established convention), don't flag every snapshot — only flag snapshots on rapidly-changing output that's likely to churn.
- If the codebase has a documented testing strategy (
TESTING.md,docs/testing.md), defer to it. The skill is the fallback when there's no project-specific guidance.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

