---
name: software-quality-practices
description: "Class-level skill covering debugging, TDD, pre-commit verification, code simplification, and throwaway spikes. The shared workflow is: investigate → test → review → ship."
version: 3.0.0
author: Hermes Agent
license: MIT
platforms: [linux, macos, windows]
metadata:
  hermes:
    tags: [debugging, testing, code-review, quality, tdd, pre-commit, spike, refactoring, software-development, language-debuggers]
    related_skills: [plan, github-pr-workflow, language-debuggers]
---

# Software Quality Practices

Class-level skill covering the five techniques that form a quality-conscious development loop: investigate before fixing, write tests first, review before shipping, simplify after shipping, and spike before committing to build. Load this umbrella when the user touches any of: debugging, testing, code review, refactoring, or experimental prototyping.

> **Golden thread:** Investigate → Test → Review → Ship. Each technique below plugs into that loop.

---

## 1. Systematic Debugging

> **Rule: No fixes without root cause investigation first.**

### Phase 1 — Root Cause Investigation

**Before attempting ANY fix:**

1. **Read errors carefully** — stack traces contain exact solutions; note line numbers, paths, codes.
2. **Reproduce consistently** — if not reproducible, gather more data instead of guessing.
3. **Check recent changes** — `git log --oneline -10` + `git diff`.
4. **Gather evidence across components** — for multi-component systems, add diagnostic logging at each boundary, run once, THEN analyze where it breaks.
5. **Trace data flow** — `search_files` upstream until you find the source of the bad value. Fix at the source, not the symptom.

**Phase 1 completion checklist:**
- [ ] Errors fully understood
- [ ] Issue reproduced consistently
- [ ] Recent changes identified
- [ ] Evidence gathered (logs, state, data flow)
- [ ] Problem isolated to specific component
- [ ] Root cause hypothesis formed

**STOP:** Do not proceed until you understand WHY it's happening.

### Phase 2 — Pattern Analysis

1. Find working examples in the same codebase (`search_files` for similar patterns).
2. Read the reference implementation COMPLETELY — don't skim.
3. List every difference between working and broken, however small.
4. Understand dependencies, config, environment, and assumptions.

### Phase 3 — Hypothesis and Testing

- Form ONE clear hypothesis: "I think X is the root cause because Y."
- Make the SMALLEST possible change to test it.
- Verify: worked → Phase 4; didn't work → NEW hypothesis.
- Don't know → say "I don't understand X" and ask the user.

### Phase 4 — Implementation

1. Create a failing regression test first (see Section 2: Test-Driven Development).
2. Fix the root cause — ONE change at a time, no "while I'm here" extras.
3. Verify: `pytest tests/test_module.py::test_regression -v` + full suite.
4. **Rule of Three:** If 3+ fixes have already failed, STOP and question the architecture. Each successive fix revealing new coupling is an architectural smell, not a code bug.

### Red Flags — STOP and Return to Phase 1

- "Quick fix for now, investigate later"
- "Just try changing X and see if it works"
- "Here are the main problems: [lists fixes without investigation]"
- "One more fix attempt" (already tried 2+)
- Each fix reveals a new problem in a different place

### Hermes Agent Tools for Debugging

- `search_files` — find error strings, trace function calls
- `read_file` — read source code with line numbers
- `terminal` — run tests, check git history, reproduce bugs
- `web_search` / `web_extract` — research error messages, library docs
- `delegate_task` — dispatch investigation subagents for multi-component bugs

---

## 2. Test-Driven Development (TDD)

> **Iron law: No production code without a failing test first.**

### Red-Green-Refactor Cycle

#### RED — Write a failing test

```python
def test_retries_failed_operations_3_times():
    attempts = 0
    def operation():
        nonlocal attempts
        attempts += 1
        if attempts < 3:
            raise Exception('fail')
        return 'success'
    result = retry_operation(operation)
    assert result == 'success'
    assert attempts == 3
```

**Good test properties:**
- One behavior per test
- Descriptive name ("and" in the name? Split it.)
- Tests real code, not mocks (unless truly unavoidable)
- Name describes behavior, not implementation

#### Verify RED — Watch it fail (MANDATORY)

```bash
pytest tests/test_feature.py::test_name -v
```

Confirm:
- Test fails for the expected reason (feature missing, not typo)
- Test passes immediately? You're testing existing behavior — fix the test.

#### GREEN — Write minimal code

```python
def add(a, b):
    return a + b  # Nothing extra
```

Cheating is OK in GREEN: hardcode returns, copy-paste, duplicate code, skip edge cases. We'll clean it in REFACTOR.

#### Verify GREEN — Watch it pass

```bash
pytest tests/test_feature.py::test_name -v
pytest tests/ -q   # No regressions
```

#### REFACTOR — Clean up

Remove duplication, improve names, extract helpers, simplify expressions. Tests stay green throughout.

### Rationalizations and Reality

| Excuse | Reality |
|--------|---------|
| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
| "I'll test after" | Tests-after passing immediately prove nothing. |
| "Already manually tested" | Ad-hoc ≠ systematic. No record, can't re-run. |
| "Deleting X hours of work is wasteful" | Sunk cost fallacy. Keeping unverified code is debt. |
| "TDD will slow me down" | TDD is faster than production debugging. |

### Integration with Systematic Debugging

Bug found? Write a failing test reproducing it. Then debug systematically. The test proves the fix and prevents regression. Never fix bugs without a test.

### Verification Checklist

- [ ] Every new function/method has a test
- [ ] Watched each test fail before implementing
- [ ] Each test failed for the expected reason
- [ ] Wrote minimal code to pass each test
- [ ] All tests pass; output pristine
- [ ] Edge cases and errors covered

---

## 3. Pre-Commit Code Verification

> **Core principle: No agent should verify its own work. Fresh context finds what you miss.**

### When to Use

After implementing a feature or bug fix, before `git commit` or `git push`. Skip only for docs-only changes, pure config tweaks, or explicit "skip verification" from the user.

### The Pipeline

#### Step 1 — Get the diff

```bash
git diff --cached   # staged changes
git diff HEAD       # working tree + staged
```

If empty, nothing to verify. If >15,000 chars, split by file with `git diff HEAD -- <file>`.

#### Step 2 — Static security scan

Scan added lines only. Any match is a concern for Step 5.

```bash
# Hardcoded secrets
git diff --cached | grep "^+" | grep -iE "(api_key|secret|password|token|passwd)\s*=\s*['\"][^'\"]{6,}['\"]"

# Shell injection
git diff --cached | grep "^+" | grep -E "os\.system\(|subprocess.*shell=True"

# Dangerous eval/exec
git diff --cached | grep "^+" | grep -E "\beval\(|\bexec\("

# Unsafe deserialization
git diff --cached | grep "^+" | grep -E "pickle\.loads?\("

# SQL injection
git diff --cached | grep "^+" | grep -E "execute\(f\"|\.format\(.*SELECT|\.format\(.*INSERT"
```

#### Step 3 — Baseline tests and linting

```bash
# Detect and run the project's test framework
python -m pytest --tb=no -q 2>&1 | tail -5   # Python
npm test -- --passWithNoTests 2>&1 | tail -5   # Node
cargo test 2>&1 | tail -5                        # Rust
go test ./... 2>&1 | tail -5                   # Go

# Linting (run only if installed)
which ruff   && ruff check . 2>&1 | tail -10
which mypy   && mypy . --ignore-missing-imports 2>&1 | tail -10
which npx    && npx eslint . 2>&1 | tail -10
which go     && go vet ./... 2>&1 | tail -10
```

**Baseline comparison:** stash changes, run, unstash, compare. Only NEW failures block.

#### Step 4 — Self-review checklist

- [ ] No hardcoded secrets, API keys, or credentials
- [ ] Input validation on user-provided data
- [ ] SQL queries use parameterized statements
- [ ] File operations validate paths (no traversal)
- [ ] External calls have error handling (try/catch)
- [ ] No debug print/console.log left behind
- [ ] No commented-out code
- [ ] New code has tests (if test suite exists)

#### Step 5 — Independent reviewer subagent

Dispatch a `delegate_task` that gets ONLY the diff + scan results. Fail-closed: unparseable = fail.

```python
delegate_task(
    goal="""You are an independent code reviewer. Review the git diff and return ONLY valid JSON.

FAIL-CLOSED RULES:
- security_concerns non-empty -> passed must be false
- logic_errors non-empty -> passed must be false
- Cannot parse diff -> passed must be false

<static_scan_results>...</static_scan_results>
<code_changes>[INSERT GIT DIFF]</code_changes>

Return ONLY this JSON:
{"passed": bool, "security_concerns": [], "logic_errors": [], "suggestions": [], "summary": ""}""",
    toolsets=["terminal"]
)
```

#### Step 6 — Evaluate and decide

- All passed → commit with `[verified]` prefix.
- Failures → report issues → Step 7 auto-fix loop (max 2 cycles).

#### Step 7 — Auto-fix loop (max 2 cycles)

Spawn a THIRD agent (not implementer, not reviewer) that fixes ONLY the reported issues. Then re-run Steps 1–6.

```bash
# After passing
git add -A && git commit -m "[verified] <description>"
```

---

## 4. Code Simplification — Parallel Review

> **Core principle: Three narrow reviewers beat one broad reviewer.**

### When to Use

After completing a set of changes, when the user says "simplify my changes", "review my recent code", or "clean up". Costs three subagents — invoke only on explicit request.

### The Process

#### Phase 1 — Capture the diff

```bash
git diff                # uncommitted changes
git diff HEAD           # if empty, include staged
git diff main...HEAD    # branch scope
```

If >2,000 changed lines, warn the user about token cost and offer to scope down.

#### Phase 2 — Three parallel reviewers

Launch `delegate_task` in batch mode (tasks array) with the FULL diff for each reviewer.

**Reviewer 1 — Code Reuse:**
Search the existing codebase for functionality the new code duplicates. Flag: new functions duplicating existing ones; hand-rolled logic that an existing utility already does. Name the existing utility and where it lives.

**Reviewer 2 — Code Quality:**
Redundant state; parameter sprawl; copy-paste-with-variation; leaky abstractions; stringly-typed code where a constant/enum/registry exists.

**Reviewer 3 — Efficiency:**
Unnecessary work (redundant computation, repeated reads, N+1 queries); missed concurrency; hot-path bloat; TOCTOU anti-patterns; memory leaks; overly broad reads.

**Rules for each reviewer:**
- Search the wider codebase for evidence — don't reason from the diff alone.
- Report as `file:line → problem → suggested fix`.
- Rank each finding `high` / `medium` / `low` confidence.
- Skip nits and style-only churn.

#### Phase 3 — Aggregate and apply

1. Merge findings, dedupe overlaps.
2. Discard false positives (you have the most context).
3. Resolve conflicts: correctness > user's stated focus > readability > micro-perf.
4. Apply surviving fixes with `patch` / `write_file` (skip if user asked for dry run).
5. Verify: run targeted tests for touched files, re-run any linter/type checker.
6. Summarize: list applied fixes by category + skipped findings and why.

---

## 5. Spike — Feasibility Experiment

> **Core principle: Validate before you build. Spikes are disposable by design.**

### When to Use

When the user wants to feel out an idea: "let me try this", "is this possible?", "compare A vs B", "before I commit to Y". Not for knowable-from-docs answers (just research) or production work (use the `plan` skill instead).

### The Loop

```
decompose → research → build → verdict
     ↑____________________________↓
              iterate on findings
```

#### 1. Decompose

Break the idea into 2–5 independent feasibility questions. Present as a table:

| # | Spike | Given/When/Then | Risk |
|---|-------|----------------|------|
| 001 | websocket-streaming | Given a WS conn, when LLM streams tokens, then client receives chunks <100ms | High |

Order by risk. The spike most likely to kill the idea runs first.

#### 2. Research (per spike, before building)

- Brief the spike in 2–3 sentences.
- Surface competing approaches in a table with pros/cons/maintenance status.
- Pick one and state why.
- Use `web_search`, `web_extract`, `terminal` to validate.

#### 3. Build

One directory per spike: `spikes/NNN-descriptive-name/`

Bias toward something the user can interact with:
1. Runnable CLI with observable output
2. Minimal HTML page demonstrating behavior
3. Small web server with one endpoint
4. Unit test with recognizable assertions

Hardcode everything — it's a spike. Avoid: complex package management, build tools, Docker, env files, config systems.

For parallel comparison spikes (e.g., 002a vs 002b), use `delegate_task(tasks=[...])`.

#### 4. Verdict

Each spike's README.md closes with:

```markdown
## Verdict: VALIDATED | PARTIAL | INVALIDATED

### What worked
- ...

### What didn't
- ...

### Surprises
- ...

### Recommendation for the real build
- ...
```

**VALIDATED** = core question answered yes, with evidence.  
**PARTIAL** = works under constraints X, Y, Z — document them.  
**INVALIDATED** = doesn't work, for this reason. A successful spike.

---

## Integration Map

How the five practices connect in a real workflow:

1. **Investigate** (`systematic-debugging`) — something broke. Reproduce, trace root cause.
2. **Test first** (`test-driven-development`) — write failing test reproducing the bug.
3. **Fix** — minimal code change to make the test pass.
4. **Review** (`requesting-code-review`) — before committing, run the verification pipeline.
5. **Commit** — `[verified]` prefix if reviewer passed.
6. **Simplify** (`simplify-code`) — after landing, optionally spawn parallel cleanup.
7. **Explore** (`spike`) — for new risky ideas, spike before writing production code.

---

## Related Skills

- `github-pr-workflow` — for PR lifecycle, code review on GitHub, and CI/CD operations.
- `plan` — for project planning and task decomposition before starting larger development work.

## Resources

- **Debugging deeper:** load `language-debuggers` for the consolidated terminal debugging reference, or read `language-debuggers/references/systematic-debugging.md` for the 4-phase methodology.
- **TDD / pre-commit / simplification / spike deeper:** load `language-debuggers`; all archived methodology references live there.
- **GitHub auth, issues, PR, and repo management:** load `github-pr-workflow`.
