agentleFS
LIXIAO199115LIXIAO199115/skillrig/skills/test-first-fix/SKILL.md

test-first-fix

Skill0 starsChanged 25 days agoMITView on GitHub

Use when fixing a bug, a failing test, or a reproducible defect in code. Turns a report into a minimal failing test, proves the failure reason, applies the smallest fix, and verifies with the full relevant suite. Also use for flaky tests and regressions after a dependency or model-driven change.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

30 rules. About 0 are common, 30 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈30

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when:

    line 22
  2. Distinctive:

    Do not use this skill for pure refactors with existing coverage.

    line 30
  3. Distinctive:

    Write down, before touching code:

    line 36
  4. Distinctive:

    If the report cannot be reproduced, stop and ask for the missing input.

    line 50
  5. Distinctive:

    Remove everything that is not required.

    line 54
  6. Distinctive:

    Add the test at the lowest level that still exercises the bug.

    line 66
  7. Distinctive:

    Read the assertion output.

    line 88
  8. Distinctive:

    If you have more than one hypothesis, rank them and test the cheapest first.

    line 100
  9. Distinctive:

    Run the single test:

    line 106
  10. Distinctive:

    Run the tests that cover direct callers.

    line 121
  11. Distinctive:

    Run the full suite once.

    line 122
  12. Distinctive:

    Run the reproduction from step 1, not only the test.

    line 123
  13. Distinctive:

    If the bug involved time, run with a fixed clock or a different time zone.

    line 124
  14. Distinctive:

    If the bug involved concurrency, run the test repeatedly.

    line 125
  15. Distinctive:

    If the bug involved a browser, check the real browser, not only the unit test.

    line 126
  16. Distinctive:

    Add a regression test that is specific enough to fail if the fix is reverted.

    line 130
  17. Distinctive:

    Update the changelog or issue with the root cause and the verification command.

    line 132
  18. Distinctive:

    Never paper over a flake with retries.

    line 136
  19. Distinctive:

    Order dependence: Run the test alone and after the suspect test.

    line 138
  20. Distinctive:

    Shared state: Look for module-level caches, singletons, database rows, environment variables.

    line 139
  21. Distinctive:

    Time: Check clocks, time zones, DST, and timeouts.

    line 140
  22. Distinctive:

    Concurrency: Look for unsynchronized writes, ports, temp files.

    line 141
  23. Distinctive:

    Resource limits: Check file descriptors, memory, and test timeouts.

    line 144
  24. Distinctive:

    Delta debugging: Remove inputs and code paths until the failure disappears.

    line 151
  25. Distinctive:

    Do not edit a test to make it pass unless the test itself is wrong.

    line 184
  26. Distinctive:

    Do not mix a behavior fix with a refactor.

    line 185
  27. Distinctive:

    Do not delete or skip a failing test to get a green run.

    line 186
  28. Distinctive:

    Do not claim a fix without the command and its output.

    line 187
  29. Distinctive:

    Do not add retries to hide a flake.

    line 188
  30. Distinctive:

    Do not test private helpers when a public behavior is available.

    line 189

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Test-First Fix

A bug fix is a claim that behavior changed in a specific way. Without a failing test, you cannot tell whether the behavior changed, whether it changed for the stated reason, or whether it will stay changed.

The sequence is fixed: reproduce, isolate, fail, fix, pass, broaden. Skipping a step produces a patch that looks right and fails later.

When to use

Use this skill when:

  • A user reports incorrect behavior with a reproducible path.
  • A test fails and the cause is not obvious.
  • A change caused a regression.
  • A test passes locally and fails in CI, or the reverse.
  • An agent proposes a fix without showing a failing test.

Do not use this skill for pure refactors with existing coverage. Use it when behavior is in question.

Workflow

1. Capture the report as facts

Write down, before touching code:

  • Exact command that triggers the bug.
  • Expected output and actual output.
  • Runtime, package manager, OS, and commit.
  • Input that matters, minimized.
  • Whether it fails every time or intermittently.
git rev-parse HEAD
node --version
npm --version

If the report cannot be reproduced, stop and ask for the missing input. Do not guess at a fix for an unreproduced bug.

2. Shrink the reproduction

Remove everything that is not required. The target is the smallest input and shortest path that still fails. Shrinking usually reveals the cause before you write any code.

For a CLI:

printf '{"amount": 0}' | node cli.mjs --format json

For a function test, call the function directly and skip the framework around it.

3. Write a failing test

Add the test at the lowest level that still exercises the bug. Assert on behavior, not on implementation details.

import test from 'node:test';
import assert from 'node:assert/strict';
import { splitAmount } from '../src/split.mjs';

test('splitAmount keeps cents when splitting three ways', () => {
  const parts = splitAmount(100, 3);
  assert.equal(parts.reduce((sum, part) => sum + part, 0), 100);
  assert.deepEqual(parts, [34, 33, 33]);
});

Name the test after the broken behavior. Run it alone:

node --test test/split.test.mjs

4. Prove the failure is the right failure

Read the assertion output. It must fail because of the bug, not because of a typo, a missing import, or an environment problem.

A test that fails with TypeError: splitAmount is not a function proves nothing about cent allocation. Fix the test setup first.

5. Form one hypothesis

State it as a sentence:

The split rounds each share down independently, so the remainder is dropped instead of being distributed.

Find the exact line. Read the surrounding code and its callers. Check the input types and units. Check assumptions about ordering, time zones, floating point, and empty collections.

If you have more than one hypothesis, rank them and test the cheapest first. Do not change code while testing a hypothesis.

6. Apply the smallest fix

Change one thing. Keep the diff reviewable. Do not refactor adjacent code, rename variables, or reformat the file.

Run the single test:

node --test test/split.test.mjs

Then run the module and the full suite:

node --test
npm run lint

7. Broaden verification

  • Run the tests that cover direct callers.
  • Run the full suite once.
  • Run the reproduction from step 1, not only the test.
  • If the bug involved time, run with a fixed clock or a different time zone.
  • If the bug involved concurrency, run the test repeatedly.
  • If the bug involved a browser, check the real browser, not only the unit test.

8. Prevent the next one

Add a regression test that is specific enough to fail if the fix is reverted. If the bug came from a missing invariant, add an assertion where the invariant is established.

Update the changelog or issue with the root cause and the verification command.

Flaky tests

Never paper over a flake with retries. First classify it.

  • Order dependence. Run the test alone and after the suspect test.
  • Shared state. Look for module-level caches, singletons, database rows, environment variables.
  • Time. Check clocks, time zones, DST, and timeouts.
  • Concurrency. Look for unsynchronized writes, ports, temp files.
  • Network. Mock or isolate the endpoint; never depend on the public internet in unit tests.
  • Randomness. Seed the generator and record the seed.
  • Resource limits. Check file descriptors, memory, and test timeouts.

A flaky test is a bug in the test or the code. Retrying it reduces the signal.

Debugging techniques that pay off

  • Bisect history. git bisect start, mark good and bad commits, run the failing test as the check.
  • Delta debugging. Remove inputs and code paths until the failure disappears; the last removed piece matters.
  • Trace instead of print. A debugger or structured trace shows state without editing the code under test.
  • Assert the invariant early. A failing assertion at the source beats a wrong value three layers later.
  • Compare working and broken runs. Diff the inputs, versions, environment, and call order.
  • Read the error twice. The first line names the failure; the stack names the path; the cause often hides in the middle.

Examples

Bug report: "CSV export sometimes writes 99 cents for a $1.00 order split three ways."

Weak fix:

// Give the remainder to the first element.
parts[0] += total - parts.reduce((sum, part) => sum + part, 0);

This hides the rounding rule in one call site and leaves the other callers wrong.

Better fix:

export function splitAmount(totalCents, ways) {
  const base = Math.floor(totalCents / ways);
  const remainder = totalCents - base * ways;
  return Array.from({ length: ways }, (_, index) => base + (index < remainder ? 1 : 0));
}

The function now owns the invariant: the parts always sum to the total. The test asserts the invariant and the distribution.

Guardrails

  • Do not edit a test to make it pass unless the test itself is wrong. If it is wrong, fix the test in a separate commit with an explanation.
  • Do not mix a behavior fix with a refactor.
  • Do not delete or skip a failing test to get a green run.
  • Do not claim a fix without the command and its output.
  • Do not add retries to hide a flake.
  • Do not test private helpers when a public behavior is available.

Verification checklist

  • Reproduction command recorded before the fix.
  • Failing test added and fails for the stated reason.
  • Test fails on the original code and passes on the fix.
  • Fix is minimal and scoped.
  • Full relevant suite passes.
  • Original manual reproduction now succeeds.
  • Regression test remains in the suite.
  • Root cause documented in the issue or changelog.

References

  • references/debugging-playbook.md contains environment, concurrency, and production-debugging checklists.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.