---
name: support-gotcha
description: "Use when a reproducible failure mode, hard-won lesson, or surprising behavior with a concrete trigger emerges and is worth preventing next time — triggered by phrases like 'record this gotcha', 'save this lesson', 'capture this finding', 'we should remember this', 'this is a gotcha', 'log this for future'. Writes a typed page to aiwiki/gotchas/{date}-{slug}.md with reproduction steps, root cause, and concrete fix; promotes to a rule when the same pattern recurs (N=3). Skip for speculation or half-formed thoughts (use /note for those); skip if the failure mode is not reproducible — a gotcha needs a concrete trigger, not a vibe."
---

# Support: Gotcha

## Overview

Every bug fixed, every failed approach, every "I wish I'd known that earlier" moment contains a lesson. This skill captures those lessons as typed wiki pages in `aiwiki/gotchas/` so future sessions hit them before the same mistake happens again.

**Core Principle:** A gotcha recorded once should prevent the same mistake forever.

**Announce at start:** "I'm using the support-gotcha skill to record this lesson learned."

## When to Use

**Automatic triggers (invoked by other skills):**
- After `support-debug` resolves a bug
- After `deliver-db-migration` encounters an issue
- After `deliver-deploy` has a rollback or complication
- After any skill encounters an unexpected failure

**Manual triggers:**
- User says "remember this" or "note this for next time"
- A non-obvious library or tool behavior is discovered
- A workaround is needed that should eventually be fixed properly
- A pattern keeps recurring across features

**Always record a gotcha when:**
- A bug took longer than expected to fix
- The fix was different from the initial hypothesis
- A library behaved differently than documented
- An environment-specific issue wasted time
- A process step was skipped and caused problems

**Do NOT write a gotcha for:** bugs in your own code that weren't framework- or pattern-related, one-off typos, problems already documented in `aiwiki/conventions/` (write the convention instead).

## When aiwiki is disabled

Before any write, check `project.aiwiki_enabled` in `.claude/CLAUDE.md` (or use `[ -d aiwiki ]` as a proxy). If aiwiki is disabled:

1. Do NOT write a gotcha file — `aiwiki/gotchas/` does not exist in this project.
2. Surface a one-line upgrade hint to the user:
   `aiwiki disabled — would have captured gotcha "{title}" to aiwiki/gotchas/{date}-{slug}.md. Re-run /setup and pick Y to enable the knowledge layer, or accept that this lesson is not durable.`
3. Continue cleanly — do NOT halt the caller. `support-debug`, `deliver-db-migration`, `deliver-deploy`, and `/forge-evolve` invoke this skill non-blockingly; they should proceed.

The global tier (`~/.claude/gotchas/`) is unaffected — it is independent of any single project's aiwiki state. If the lesson is truly universal, write to the global tier per Step 2 below; otherwise the upgrade hint is the user's signal to enable aiwiki if they want project-scoped capture.

## I/O Contract

| Field | Value |
|---|---|
| **Requires** | Concrete lesson: what broke, why, how to prevent |
| **Produces** | Gotcha page in `aiwiki/gotchas/{YYYY-MM-DD}-{slug}.md` (project) or `~/.claude/gotchas/{YYYY-MM-DD}-{slug}.md` (global) |
| **Schema** | `aiwiki/schemas/gotcha.md` — LINT validates frontmatter + sections on write |
| **Feeds into** | Future sessions (prevention), `gotcha-hunter` agent (surfaces relevant entries to reviewers), `/forge-evolve` (skill improvement) |

### Storage tiers

| Tier | Location | Scope | When |
|---|---|---|---|
| Project | `aiwiki/gotchas/` | This codebase only | Issue specific to this stack, patterns, or config |
| Global | `~/.claude/gotchas/` | All future projects | Issue applies universally — library behavior, general pattern |

When uncertain, default to project. The gotcha can be promoted later by writing a global copy and retiring the project one.

## The Gotcha Process

### Step 1: Extract the lesson

Three pieces of information must be concrete:

1. **What broke** — the failure or surprise, not the symptom
2. **Root cause** — what was actually wrong, not what looked wrong
3. **Prevention** — actionable steps ("Check X before doing Y"), not "be more careful"

### Step 2: Classify project vs global

| Question | Answer |
|---|---|
| Would this happen in a different project with a different stack? | Yes → global |
| Is this about a specific library version, config, or this project's architecture? | Yes → project |
| Is this about Docker, CI, or deployment in general? | Yes → global |

When genuinely ambiguous, ask the user: "Project-specific or universal?"

### Step 3: Search for existing matches

Before writing a new file, grep `aiwiki/gotchas/` (and `~/.claude/gotchas/` for global) for the same pattern. If a matching gotcha exists:

- Bump its frontmatter `occurrences` field by 1
- Update `status` per the auto-promotion path (see Step 5): 1 → `active`, 2 → `watch`, 3 → `promotion-pending`
- Append a one-line entry under `## Reproducer` if the new occurrence has a different reproducer

Do NOT write a duplicate file.

### Step 4: Write the gotcha page

**File naming:** `{YYYY-MM-DD}-{slug}.md`. Slug is kebab-case, ≤8 words, describes the pattern (not the symptom).

**Schema-aligned frontmatter:**

```yaml
---
schema_id: gotcha
schema_version: 1
severity: low | medium | high | critical
date: YYYY-MM-DD
occurrences: 1
status: active
---
```

**Required sections** (LINT will reject the file if any are missing or out of order):

| Section | Content | Citation |
|---|---|---|
| `## What broke` | One sentence — what failed and where | Cite the failure point |
| `## Reproducer` | Minimal steps or code link | Cite if linking to an existing test |
| `## Root cause` | What was actually wrong | Cite with `file:line@<sha7>` |
| `## Fix` | The resolution | Cite the fix code |
| `## Prevention` | Rule, check, or pattern that prevents recurrence | Cite a convention or rule if one exists |

**Line caps:** hard cap 150 lines, soft target 50-100. A gotcha that needs more than 150 lines is two gotchas, or it's narrative debug logs that don't belong here.

**Citations:** every section needs at least one. Code references use `file:line@<sha7>` form. LINT auto-fills missing `@<sha7>` on first save.

Skeleton example: see `aiwiki/schemas/gotcha.md`.

### Step 5: Auto-promotion path (1 → 2 → 3 occurrences)

The `occurrences` field drives `status` automatically. Step 3 bumps the count; this step records the corresponding state transition.

| Occurrences | `status` | What happens |
|---|---|---|
| 1 | `active` | Newly recorded. Default state. |
| 2 | `watch` | Pattern is accumulating. `gotcha-hunter` surfaces as "one more triggers promotion". No rule drafted yet. |
| 3 | `promotion-pending` | Auto-draft a proposed rule into frontmatter (see below). The next session-start surfaces this as a hard-interrupt. |
| 4+ | stays `promotion-pending` | Until the user reviews (Step 6), additional occurrences keep bumping `occurrences` but `status` stays at `promotion-pending`. |

**When occurrences hits 3 — draft the proposed rule (mechanical only):**

1. Set frontmatter `status: promotion-pending`.
2. Identify the target rule file from the **Promotion paths** table below.
3. Draft a one-to-three-sentence actionable rule based on the `## Prevention` section. Keep it imperative ("must / never / always") so it reads as a rule, not a tip.
4. Add a `proposed_rule:` block to the frontmatter:

   ```yaml
   proposed_rule:
     rule_path: rules/common/{relevant-rule}.md
     draft: |
       {actionable rule statement}
     drafted_at: YYYY-MM-DD
   ```

5. Surface to the user: "3rd occurrence of <title> recorded. Proposed rule drafted in frontmatter; awaiting review at next session start."

Do not edit any rule file at this step. Drafting is mechanical; promotion is reviewed at Step 6.

**Promotion paths:**

| Current page | Promotion target | When |
|---|---|---|
| Project gotcha (`aiwiki/gotchas/`) | Same gotcha as global (`~/.claude/gotchas/`) | Same pattern seen in a different project |
| Project gotcha at occurrences=3 | Project rule (`.claude/rules/<area>.md`) — typically a phase-conditional or always-on rule | 3× in same project |
| Global gotcha at occurrences=3 | Common rule (`rules/common/<area>.md`) | 3× across projects |

### Step 6: Session-start review of pending promotions

When session-start surfaces a gotcha with `status: promotion-pending` (always carries a `proposed_rule:` block), do not start new work. Process each one:

1. Read the gotcha's `## Prevention` section and the `proposed_rule.draft` value.
2. Ask the user: approve, defer, or reject.

**On approve:**
- Append the `draft` rule text to the file at `proposed_rule.rule_path` (create the file if needed; match its existing list/heading style).
- Invoke `support-skill-validator` against the modified rule file. If it reports a contradiction with existing rules, revert the rule-file edit and surface the contradiction. The user resolves the conflict and re-runs this step.
- On validator pass: change the gotcha's frontmatter `status: promotion-pending` → `status: promoted-to-rule`. Remove the `proposed_rule:` block. Cross-reference the rule path under `## Prevention`.
- Also update any other gotchas that share the same pattern with a `Promoted to rule: <rule_path>` line under `## Prevention`.

**On defer:** Leave `status: promotion-pending` and the `proposed_rule:` block in frontmatter. Append a `defer:` field with the user's rationale and date. Next session-start surfaces it again.

**On reject:** Change frontmatter `status: promotion-pending` → `status: rejected`. Remove the `proposed_rule:` block. Add a one-line note under `## Prevention`: "Proposed rule rejected ({date}): {reason}". The gotcha is retained as a recorded lesson, but session-start will not re-prompt and `gotcha-hunter` skips it at session-start scope.

### Step 7: Retiring a gotcha

When a gotcha no longer applies (framework upgrade made it impossible, code path was removed, library fixed the underlying issue):

- Change frontmatter `status` → `status: retired` (regardless of prior state)
- Add a one-line note under `## Prevention`: "Retired ({date}): {reason}"

LINT preserves retired entries — they're still searchable history. `gotcha-hunter` skips retired entries when surfacing context to reviewers.

## Severity classification

| Severity | Criteria | Example |
|---|---|---|
| `critical` | Data loss, security breach, production outage | Migration deletes production data |
| `high` | Significant time waste (1+ hour), incorrect behavior shipped | Subtle race condition in auth |
| `medium` | Moderate time waste (15-60 min), development friction | Docker seed duplication |
| `low` | Minor inconvenience, cosmetic, easily worked around | Wrong import path convention |

## Status values (schema-canonical)

The unified status vocabulary spans the auto-promotion path and manual retirement. Transitions are governed by `occurrences` (auto) and user review (manual). `gotcha-hunter` and `support-wiki-lint` both validate against this enum.

| Status | Meaning | How entered |
|---|---|---|
| `active` | Newly recorded; `occurrences = 1`. Default state. | Step 4 writes a new gotcha. |
| `watch` | `occurrences = 2`. Pattern is accumulating; one more occurrence triggers promotion drafting. | Step 3 bumps a 1× gotcha to 2×. |
| `promotion-pending` | `occurrences = 3` (or more, until reviewed). A `proposed_rule:` block is attached. Awaits user review at next session-start. | Step 5 auto-drafts. |
| `promoted-to-rule` | The proposed rule was approved and appended to a rule file. Gotcha retained as cross-referenced provenance. | Step 6 approve path. |
| `rejected` | User reviewed a `promotion-pending` gotcha and declined promotion. Do not re-prompt. Lesson stays recorded; `gotcha-hunter` skips at session-start scope. | Step 6 reject path. |
| `retired` | Gotcha no longer applies (framework upgrade, code path removed, library fixed). Kept for searchable history; `gotcha-hunter` skips. | Step 7 manual. |

## Session-start integration

A session-start hook scans `aiwiki/gotchas/INDEX.md` (and `~/.claude/gotchas/INDEX.md`) for rows whose `status` cell is `promotion-pending` and surfaces them as a hard-interrupt before new work begins. Step 6 is the gate between "draft rule" and "ship rule".

When no pending promotions exist, `gotcha-hunter` (separate agent) reads `aiwiki/gotchas/` to surface diff-relevant entries to reviewers in the `quality-code-review` chain.

## Red flags — record a gotcha

| Situation | Why |
|---|---|
| "Oh, I've seen this before" | If you've seen it and it isn't recorded, it will happen again |
| Fix took more than 3 attempts | The debugging path itself is worth recording |
| Library behaved differently than documented | Future sessions will hit this too |
| "Works on my machine" issues | Environment differences are always gotcha-worthy |
| Skipped a process step and paid for it | Process-compliance lessons are valuable |
| Found a workaround instead of a fix | Both the workaround and the underlying issue need documenting |
| Deployment had unexpected complications | Deployment lessons prevent outages |
| Test was flaky or timing-dependent | Document the pattern; flaky tests erode confidence |

## Integration with other skills and agents

| Skill / agent | Interaction |
|---|---|
| `support-debug` | Invokes this skill after a successful fix |
| `deliver-db-migration` | Records migration-related lessons |
| `deliver-deploy` | Records deployment-related lessons |
| `deliver-onboarding` | Reads `aiwiki/gotchas/` to populate troubleshooting sections |
| `support-skill-validator` | Validates promoted rules at the approve step (Step 6) |
| `gotcha-hunter` (agent) | Reads `aiwiki/gotchas/` and surfaces diff-relevant entries to reviewers; never writes |
| `support-wiki-lint` | Validates frontmatter + sections on every write |
| `support-dream` | At phase-close / PreCompact: consolidates raw entries, may merge similar gotchas, prunes retired entries (writes proposals to `aiwiki/proposed/{dream_id}/` for user review) |
| `/forge-evolve` command | Reads gotchas to identify skill improvement opportunities |

## Quick reference

| Step | Action | Output |
|---|---|---|
| 1. Extract | What broke / root cause / prevention | Three-part lesson |
| 2. Classify | Project or global? | Storage location chosen |
| 3. Search | Existing matching gotcha? | Bump `occurrences` instead of writing duplicate |
| 4. Write | Schema-aligned page with required frontmatter + 5 sections | `aiwiki/gotchas/{YYYY-MM-DD}-{slug}.md` (`status: active`) |
| 5. Auto-promote | 1 → `active`; 2 → `watch`; 3 → `promotion-pending` + `proposed_rule:` block | Awaits session-start review |
| 6. Session-start review | Approve / defer / reject the proposed rule | `promoted-to-rule`, deferred (stays `promotion-pending`), or `rejected` |
| 7. Retire (when no longer applies) | Set `status: retired` | Kept as searchable history |
