# Fix Bug

> Fix a bug from a GitHub issue using TDD. Analyzes the issue, creates a reproducing test case, implements the fix, verifies it, and creates a PR. Use this skill whenever working on a GitHub issue, bug report, or regression — even if the user just provides an issue number or URL.

- Skill: `pgplex/fix-bug` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pgplex/fix-bug`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pgplex/fix-bug/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: pgplex (https://skillmd.com/u/pgplex)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pgplex/fix-bug

---


# Fix Bug

TDD workflow for fixing bugs from GitHub issues: reproduce first, then fix, then verify.

**Two principles to keep turnaround fast:**

1. **Run only the impacted/relevant tests locally** — never the full suite. CI runs the full suite on the PR; locally you only need to confirm the reproducing test and its immediate neighbors. Narrow `PGSCHEMA_TEST_FILTER` to the specific case (or category) you touched.
2. **Prefer folding `testdata/diff` cases into existing ones** — each test case is its own embedded-postgres apply cycle, so every new directory adds to total test time. By default, consider adding the scenario to a related existing case. Only create a new case when that's more reasonable or consistent with the existing layout (see Phase 2).

## Phase 1: Analyze

1. Fetch the issue: `gh issue view <number>`
2. Classify the bug:
   - **Dump bug**: `pgschema dump` produces wrong output → test in `testdata/dump/`
   - **Diff/Plan bug**: dump is correct but plan generates wrong DDL → test in `testdata/diff/`
   - **Both**: start with dump; if dump is correct, it's a diff bug

## Phase 2: Create Test Case (Red)

### Dump Bugs

Create `testdata/dump/issue_<N>_<description>/` with:
- `manifest.json` — metadata with name, description, source URL, notes
- `raw.sql` — original DDL
- `pgdump.sql` — what pg_dump produces (input to test)
- `pgschema.sql` — expected correct output

Register in `cmd/dump/dump_integration_test.go`:
```go
func TestDumpCommand_Issue<N><Description>(t *testing.T) {
    if testing.Short() { t.Skip("Skipping integration test in short mode") }
    runExactMatchTest(t, "issue_<N>_<description>")
}
```

Verify it fails: `go test -v ./cmd/dump -run TestDumpCommand_Issue<N>`

### Diff/Plan Bugs

Categories: `create_table`, `create_index`, `create_trigger`, `create_view`, `create_function`, `create_procedure`, `create_sequence`, `create_type`, `create_domain`, `create_policy`, `create_materialized_view`, `comment`, `privilege`, `default_privilege`, `dependency`, `online`, `migrate`.

**Decide: fold or create new (default: consider folding).** Each case directory is a separate embedded-postgres apply cycle, so folding keeps the suite fast.

- **Fold** when the bug is a natural variation of an existing case (same object type/category) and adding the DDL doesn't obscure that case's intent. Add the reproducing statements to the existing `old.sql`/`new.sql`, then regenerate its expected outputs. Browse the category first (`ls testdata/diff/<category>/`) to find the best home.
- **Create new** (`testdata/diff/<category>/issue_<N>_<description>/` with `old.sql` and `new.sql`) when the scenario is distinct, or when a standalone `issue_<N>` case is more consistent with how the category is organized.

Verify it fails (use the case name you folded into, or the new `issue_<N>_<description>`):
```bash
PGSCHEMA_TEST_FILTER="<category>/<case>" go test -v ./internal/diff -run TestDiffFromFiles
```

Generate expected outputs once you know correct behavior:
```bash
PGSCHEMA_TEST_FILTER="<category>/<case>" go test -v ./cmd -run TestPlanAndApply --generate
```

## Phase 3: Fix (Green)

Common locations:
- **Dump**: `ir/inspector.go`, `ir/normalize.go`, `internal/dump/`
- **Diff**: `internal/diff/` (`table.go`, `column.go`, `index.go`, `trigger.go`, `view.go`, `function.go`, `procedure.go`, `sequence.go`, `type.go`, `policy.go`, `constraint.go`)
- **IR**: `ir/ir.go`, `ir/quote.go`

Make the minimal fix. Use **pg_dump** and **postgres_syntax** skills as needed.

## Phase 4: Verify

Run only the impacted tests (not the full suite — CI runs that on the PR). Keep the filter as narrow as possible:
```bash
# Dump bugs
go test -v ./cmd/dump -run TestDumpCommand_Issue<N>

# Diff bugs — start with the specific case
PGSCHEMA_TEST_FILTER="<category>/<case>" go test -v ./internal/diff -run TestDiffFromFiles
PGSCHEMA_TEST_FILTER="<category>/<case>" go test -v ./cmd -run TestPlanAndApply
```

Only widen the filter to the whole category (`PGSCHEMA_TEST_FILTER="<category>/"`) if the fix touched shared diff logic that could affect sibling cases.

## Phase 5: Create PR

```bash
git checkout -b fix/issue-<N>-<description>
git add <files>
git commit -m "fix: <description> (#<N>)"
git push -u origin fix/issue-<N>-<description>
gh pr create --title "fix: <description> (#<N>)" --body "## Summary
<what was broken and how it was fixed>

Fixes #<N>

## Test plan
<what test was added and how to run it>"
```

## Checklist

- [ ] Bug classified (dump vs diff)
- [ ] Test case folded into an existing case, or new `issue_<N>_<description>` created when more reasonable
- [ ] Test fails before fix (red)
- [ ] Minimal fix implemented
- [ ] Test passes after fix (green)
- [ ] Impacted tests pass locally (narrow filter — full suite left to CI)
- [ ] PR created and linked to issue

