Files
AgentCompany/builtin-skills/software-engineer/clean-tests/SKILL.md
T
ZacharyZhang-NY e74dafcd36 Silence render-disposed spam, unbreak bundled skills, drop board wake
Three independently ugly bugs that all fed the same log flood:

* Electron's `WebFrameMain.send` writes every "Render frame was disposed"
  to stderr via `console.error`. When the worker fans a second
  agent-event past a reloading renderer, we produced thousands of
  identical lines and stalled the main loop on stderr I/O. Added a
  `mainFrame.detached` guard so the send is short-circuited before
  Electron's own catch block fires, and installed a targeted filter in
  `suppress-warnings.ts` (now actually imported from the entry point)
  so the specific `console.error("Error sending from webFrameMain:"…)`
  noise never reaches the terminal. Any other payload still surfaces.

* `dispatchAgentMessage` accepted the human-reader sentinel as a wake
  target when an agent replied to `__board__` — `wakeAgentIfPossible`
  then called `db.getAgent("__board__")`, threw, and the dispatcher
  kept repeating on every send. Strip `HUMAN_SENDER_ID` at
  `resolveMessageWakeTargets`, and keep a defense-in-depth guard in the
  heartbeat so a stray caller (future automation fan-out) cannot
  reintroduce the crash.

* Pi's skills loader rejects any SKILL.md whose frontmatter `name`
  diverges from its parent directory. The four bundled `my-writing-style`
  copies declared `name: writing-style-skill`, so every install emitted
  a warning and dropped the skill. They also lived as orphan gitlinks
  (mode 160000 without .gitmodules), and the global `*.md` gitignore
  hid every other SKILL.md from version control. Fixed the frontmatter
  to match the dir, converted the dangling submodules to plain trees,
  and allowlisted `builtin-skills/**/*.md` so a fresh clone carries the
  catalogue instead of silently losing it.
2026-04-20 08:13:48 -04:00

4.9 KiB

name, description
name description
clean-tests Use when writing, fixing, editing, or refactoring Python tests. Enforces Clean Code principles—fast tests, boundary coverage, one assert per test.

Clean Tests

T1: Insufficient Tests

Test everything that could possibly break. Use coverage tools as a guide, not a goal.

# Bad - only tests happy path
def test_divide():
    assert divide(10, 2) == 5

# Good - tests edge cases too
def test_divide_normal():
    assert divide(10, 2) == 5

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        divide(10, 0)

def test_divide_negative():
    assert divide(-10, 2) == -5

T2: Use a Coverage Tool

Coverage tools report gaps in your testing strategy. Don't ignore them.

# Run with coverage
pytest --cov=myproject --cov-report=term-missing

# Aim for meaningful coverage, not 100%

T3: Don't Skip Trivial Tests

Trivial tests document behavior and catch regressions. They're worth more than their cost.

# Worth having - documents expected behavior
def test_user_default_role():
    user = User(name="Alice")
    assert user.role == "member"

T4: An Ignored Test Is a Question About an Ambiguity

Don't use @pytest.mark.skip to hide problems. Either fix the test or delete it.

# Bad - hiding a problem
@pytest.mark.skip(reason="flaky, fix later")
def test_async_operation():
    ...

# Good - either fix it or document why it's skipped
@pytest.mark.skip(reason="Requires Redis, see CONTRIBUTING.md for setup")
def test_cache_invalidation():
    ...

T5: Test Boundary Conditions

Bugs congregate at boundaries. Test them explicitly.

def test_pagination_boundaries():
    items = list(range(100))
    
    # First page
    assert paginate(items, page=1, size=10) == items[0:10]
    
    # Last page
    assert paginate(items, page=10, size=10) == items[90:100]
    
    # Beyond last page
    assert paginate(items, page=11, size=10) == []
    
    # Page zero (invalid)
    with pytest.raises(ValueError):
        paginate(items, page=0, size=10)
    
    # Empty list
    assert paginate([], page=1, size=10) == []

T6: Exhaustively Test Near Bugs

When you find a bug, write tests for all similar cases. Bugs cluster.

# Found bug: off-by-one in date calculation
# Now test ALL date boundaries
def test_month_boundaries():
    assert last_day_of_month(2024, 1) == 31  # January
    assert last_day_of_month(2024, 2) == 29  # Leap year February
    assert last_day_of_month(2023, 2) == 28  # Non-leap February
    assert last_day_of_month(2024, 4) == 30  # 30-day month
    assert last_day_of_month(2024, 12) == 31 # December

T7: Patterns of Failure Are Revealing

When tests fail, look for patterns. They often point to deeper issues.

# If all async tests fail intermittently,
# the problem isn't the tests—it's the async handling

T8: Test Coverage Patterns Can Be Revealing

Look at which code paths are untested. Often they reveal design problems.

# If you can't easily test a function, it probably does too much
# Refactor for testability

T9: Tests Should Be Fast

Slow tests don't get run. Keep unit tests under 100ms each.

# Bad - hits real database
def test_user_creation():
    db = connect_to_database()  # Slow!
    user = db.create_user("Alice")
    assert user.name == "Alice"

# Good - uses mock or in-memory
def test_user_creation():
    db = InMemoryDatabase()
    user = db.create_user("Alice")
    assert user.name == "Alice"

Test Organization

F.I.R.S.T. Principles

  • Fast: Tests should run quickly
  • Independent: Tests shouldn't depend on each other
  • Repeatable: Same result every time, any environment
  • Self-Validating: Pass or fail, no manual inspection
  • Timely: Written before or with the code, not after

One Concept Per Test

# Bad - testing multiple things
def test_user():
    user = User("Alice", "alice@example.com")
    assert user.name == "Alice"
    assert user.email == "alice@example.com"
    assert user.is_valid()
    user.activate()
    assert user.is_active

# Good - one concept each
def test_user_stores_name():
    user = User("Alice", "alice@example.com")
    assert user.name == "Alice"

def test_user_stores_email():
    user = User("Alice", "alice@example.com")
    assert user.email == "alice@example.com"

def test_new_user_is_valid():
    user = User("Alice", "alice@example.com")
    assert user.is_valid()

def test_user_can_be_activated():
    user = User("Alice", "alice@example.com")
    user.activate()
    assert user.is_active

Quick Reference

Rule Principle
T1 Test everything that could break
T2 Use coverage tools
T3 Don't skip trivial tests
T4 Ignored test = ambiguity question
T5 Test boundary conditions
T6 Exhaustively test near bugs
T7 Look for patterns in failures
T8 Check coverage when debugging
T9 Tests must be fast (<100ms)