Skip to content
Back to skills

Extract Failures From History

ASecurity

Round-two: recurring failure themes from git log (revert, hotfix, incident, rollback), written as `### ` entries in ai/failures/_index.md — never per-theme files.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 20, 2026
ai-agentsgobashgitapi

Works with

  • api

Security analysis

A100/100

Scanned October 20, 2026

npx -y skills add adnanmokhtar/refract --skill extract-failures-from-history --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Extract Failures From History?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Extract Failures From History
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adnanmokhtar-extract-failures-from-history/badge)](https://www.skillsdirectory.com/skills/adnanmokhtar-extract-failures-from-history)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: extract-failures-from-history
description: "Round-two: recurring failure themes from git log (revert, hotfix, incident, rollback), written as `### ` entries in ai/failures/_index.md — never per-theme files."
kind: skill
pack: learning
allowed-tools: [Read, Write, Edit, Grep, Glob, Bash]
---

# Skill: extract-failures-from-history

## Purpose

A failure that happened once is a story. A failure family that happened 4 times across 18 months is an architectural lesson. Round-two reads the project's actual history and finds the lessons.

The output drives the theme entries Phase 4.7-DEEP appends to `ai/failures/_index.md` — the ONE append-only don't-retry catalog, which architectural agents must inject in pre-flight (per Hard Rule). **Never a per-theme file**: `ai/failures/<theme>.md` is forbidden by `/learn-from-task` step 6, `knowledge-curator` § 4b and [`learning-sink.md`](../../../../snippets/learning-sink.md); a second file is a catalog no consumer reads. Each theme is one `### <YYYY-MM-DD> — <theme>` entry in the index, in the shape its `## Format per entry` declares. Without this step, agents propose ideas that already failed in this codebase.

## Premise

- Real source is the truth. Read every candidate commit's subject + body + diff before classifying it into a theme — keyword matches alone are not classifications.
- Every recorded theme cites the SHAs of its representative commits + the top affected files; every prevention-checklist bullet maps to evidence visible in those commits.
- Customer names, dollar amounts, and individual-engineer blame are sanitized (per the privacy rule) — institutional learning, not blame.
- Empty extraction is honest — `## No recurring failure themes` is a valid output for a young codebase or a clean main branch.
- Fabrication — promoting one revert to a "recurring theme", inventing a `cache-invalidation` lesson when no commit mentions caching, naming individual engineers — corrupts every architectural agent's pre-flight injection.

## Mechanical halt

- Hand-wave in failure output — `etc.`, `...`, `several commits`, `appears to be a pattern`, `the team often`, a theme without ≥`min_theme_occurrences` SHA citations, a `root_cause_family` not grounded in a diff you read — REFUSE to write the row.
- Re-read the diffs and regenerate the theme, OR relegate the finding to `one_off_failures`.
- If git is inaccessible OR commit count is below 30 OR no theme reaches `min_theme_occurrences`, write `<NOT-DETECTED: failures: <reason — git unavailable | <N> commits below 30 | no themes ≥ <min>>>` per the WEAK gate.
- Never invent themes from training-data familiarity with the listed taxonomy — only what the actual git history shows counts.

## When to use

- `/setup-project --refine` Phase 2.12 — once per project.
- Manually when a developer says "we keep hitting <X> bug" and wants the failure recorded as a permanent lesson.

## Inputs

- `lookback` (default: 24 months OR all-time if repo is younger).
- `min_theme_occurrences` (default: 2 — a theme must have ≥ 2 instances to count as recurring).
- `extra_doc_paths` (optional) — explicit user-opted paths like `docs/postmortems/`, `docs/incidents/`, `INCIDENTS.md`. Skipped unless user passes `--include-incidents=<path>` to setup-project.
- `output_section` — section path (default: `## Failure history`).

## Procedure

### Step 1 — Establish git accessibility

Run `git log --oneline -1`. If git not accessible (no `.git/`, shallow clone, permission error), abort with `[REFINE-WEAK: failures — git not accessible]`.

Run `git log --pretty=format:'%h %s' --since='<lookback> ago' | wc -l`. Need ≥ 30 commits to do useful theme grouping. If less, flag `[REFINE-WEAK: failures — too few commits]`.

### Step 2 — Sweep commit messages for failure-marker keywords

Search the git log for case-insensitive matches:

```bash
git log --pretty=format:'%h%x00%s%x00%b%x00%cd%x00%an' --since='<lookback> ago' \
  | grep -iE '\b(revert|hotfix|incident|regression|rollback|outage|fix.{0,30}critical|fix.{0,30}prod|emergency|p0|p1|sev1|postmortem|fire|broke|broken|outage|panic|crash|leak|exploit|vulnerability)\b'
```

For each match, extract:
- SHA (`%h`).
- Subject (`%s`) — first line.
- Body (`%b`) — full description (often empty; sometimes contains the cause).
- Date (`%cd`).
- Author (`%an`) — for grouping context, NOT for blame.

### Step 3 — Read the diffs

For each candidate commit, run `git show --stat <sha>` to get the affected files. For commits with non-trivial diffs (< 200 LOC changed), run `git show <sha>` and skim the actual change.

Note:
- Affected files (top 3 by lines changed).
- Affected functions (if extractable from hunk headers).
- Type of change: revert? add validation? add null-check? add transaction? add idempotency? change query plan? add index? cache-related? auth-related?

### Step 4 — Group commits by theme

Define candidate themes:

| Theme | Markers |
|---|---|
| `auth-bypass` | auth, permission, role, token, jwt, session |
| `n-plus-1-perf` | N+1, slow query, timeout, prefetch, eager-load, query optimization |
| `migration-drift` | migration, schema, rename, drop column, foreign key |
| `payment-double-charge` | payment, charge, refund, idempotency, duplicate |
| `tenant-leak` | tenant, multi-tenant, cross-tenant, scope |
| `cache-invalidation` | cache, stale, TTL, invalidat |
| `concurrency-bug` | race, concurrent, deadlock, lock, transaction-isolation |
| `null-handling` | null, NoneType, undefined, NPE |
| `rate-limit-storm` | rate-limit, throttle, retry-storm, thundering-herd |
| `data-loss` | data-loss, accidentally deleted, bug deletes |
| `email-blast` | email, notification, accidentally sent |
| `secret-leak` | secret, credential, key leaked, token exposed |
| `external-api-flake` | webhook, vendor, retry, idempotency-key, third-party |

Group each commit into the theme with the strongest marker match. If multiple match, pick by the commit body's specifics, then by file path (e.g. `app/auth/*` → `auth-bypass`). If none match, group as `other` (don't lose them).

### Step 5 — Cross-reference with existing `ai/failures/_index.md`

If round-one wrote any failures (e.g. via `business-auditor` running before REFINE), read them. For each existing failure:

- Check if the new extraction surfaces it (avoid double-counting).
- Check if the existing failure has a `status:` field (`active`, `validated_failure`, `superseded_by_<adr>`). Append-only — never modify existing.

### Step 6 — Filter to recurring themes

Apply `min_theme_occurrences` (default 2). A theme with 1 instance is a story; ≥ 2 is a recurring lesson. Single-occurrence findings go into `## one-off failures` (not promoted to a theme entry in `ai/failures/_index.md`).

### Step 7 — Read postmortem docs (opt-in only)

If `extra_doc_paths` was provided AND those paths exist:

- Read every `.md` in those paths.
- Look for headings like `## Cause`, `## Resolution`, `## Prevention`, `## Lessons`.
- Map each to a theme.
- Cross-reference with the git-extracted commits (often the postmortem mentions the SHA).

This step requires explicit opt-in via `--include-incidents=<path>` because postmortem docs are sensitive — they may contain customer references, internal financial impact, blameful language.

### Step 8 — Output

Write to `.claude/_refine-extract.md` under `## Failure history`:

```yaml
extraction_date: <YYYY-MM-DD>
git_lookback: <duration>
total_commits_scanned: <N>
candidate_failure_commits: <N>
strong_signals: ["recurring-themes", "diff-analyzed", "postmortems-read"]   # subset present

recurring_themes:
  - theme: <e.g. auth-bypass>
    occurrences: <N>
    status: active  # default; user can change to validated_failure / superseded_by_ADR-NNNN later
    affected_files:
      - <file/path/sample.py>     # top 3 by frequency across the N commits
    commits:
      - { sha: <hash>, subject: <subject>, date: <YYYY-MM-DD> }
      # ≤ 5 representative commits; the full list goes in the theme's entry in ai/failures/_index.md if STRONG
    root_cause_family: |
      <2-3 sentence narrative — what kind of mistake recurs.
       e.g. "Permissions checked at controller layer but bypassed when service layer
       was called from a background job that didn't propagate the user context."
       Cite the most representative commit's diff inline.>
    prevention_checklist:
      - <bullet — concrete check a future agent can run in pre-flight>
      - <bullet>
    cited_postmortem: <docs/postmortems/2025-04-15-cross-tenant-data.md>   # if extra_doc_paths set

one_off_failures:
  - { theme: <name>, sha: <hash>, subject: <subject>, date: <YYYY-MM-DD> }
  # short list — recorded for completeness, not promoted to a theme entry
```

## Quality gate

- **STRONG**: ≥ 2 recurring themes (each with ≥ 2 occurrences) extracted, ≥ 1 with prevention-checklist of ≥ 3 bullets.
- **MEDIUM**: 1 recurring theme found. Consumed, not discarded: Phase 4.7-DEEP appends the one theme entry to `ai/failures/_index.md`, and `compute-anchor-density § Step 4` counts `failure-theme` for a block that cites it (STRONG **or** MEDIUM counts; only WEAK is excluded). One theme with diffs read and SHAs cited is real signal — the tier exists to say "thin, not absent", which is a different fact from both neighbours.
- **WEAK**: 0 recurring themes (codebase too young, history too clean, or the team lands all hotfixes outside main). Output `## No recurring failure themes` finding. Phase 4.7-DEEP appends no theme entry in this case.

## Privacy / safety

- **Never extract failure-history content from any source the user hasn't opted into.** The skill stays inside the repo + local git log; no external bug-tracker calls; no Sentry / Datadog API calls; no Slack scraping.
- **Postmortem docs are opt-in** via `--include-incidents=<path>`. Default: skip.
- **Customer names / specific dollar amounts / individual employee blame** must NOT appear in the output. If the extraction encounters them in commit messages or postmortem bodies, sanitize:
  - Replace customer names with `<customer>`.
  - Replace dollar amounts with `<significant-amount>`.
  - Strip individual names from blame contexts (group context — "the team / engineer who ..." — is fine).
- **Public repo check**: if the project's git remote is a public host (github.com/<user>/<repo> with public visibility), warn the user before appending theme entries to `ai/failures/_index.md`, since the file is committed and visible. The user can choose to gitignore `ai/failures/` or proceed.

## Anti-patterns

- **Treating every revert as an incident** — many reverts are benign (deps update broke build, revert, fix forward in 10 minutes). Read the diff before classifying.
- **Calling a single-occurrence bug a recurring failure** — the threshold is the protection. A one-off is a story; a pattern is a lesson.
- **Inventing a theme not represented in commits** — if `cache-invalidation` doesn't appear in any commit, it's not a finding.
- **Naming individual engineers** — failure catalog is institutional learning, not blame. Strip names.
- **Extracting from a fork's `main` while ignoring `develop`** — many shops do hotfixes on `main` and feature work on `develop`. Walk both.
- **Skipping commit bodies** — the subject is often terse ("fix bug"); the body has the details. Always read both.

## Verify (postconditions)

Contract: [`templates/snippets/postcondition-check.md`](../../../../snippets/postcondition-check.md). **Preconditions are not verification** — this skill guards its entry and must also check its exit. Every entry under `## Failure modes` that this skill's own write could cause becomes a check here, observed rather than assumed, reported when it passes as well as when it fails, and `NOT RUN` with a reason where no signal is available. A failure mode the skill documented and never checked is a risk it accepted on the caller's behalf without saying so.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.