Skip to content

Every incident becomes a test. Git history proves it.

Aftershock reads an incident and the commit that fixed it, drafts a regression test, and keeps it only if it fails before the fix and passes on it. From then on it runs on every pull request, so the same outage can’t ship twice.

Free for open source and teams up to five committers. Apache 2.0.

INC-12 · live
  1. IncidentPayment retry charged a customer twiceGitHub issue #41
  2. Draft
    def test_retry_after_lost_ack_does_not_double_charge(client, order):    first = client.post("/payments", json={"order_id": order.id})    retry = client.post("/payments", json={"order_id": order.id})    assert retry.status_code == 400    assert payment_count(order.id) == 1
    AI draft, not trusted yet
  3. Before the fix3f2a1c9fails 3 of 3: the bug reproduces
  4. Time travelaa86f56fix: reject duplicate pending payment
  5. On the fixaa86f56proven
  6. Pull request#214 refactor paymentsRecur: INC-12 would ship again
INC-12 comes in from a GitHub issue: a payment retry charged a customer twice.

One loop, and only one step trusts a model.

The model writes the first draft. Real test runs against your own git history decide what stays.

  1. An incident comes in

    From a labelled GitHub issue, a pasted postmortem, a form, or an alert from Sentry, PagerDuty, incident.io and others. Alerts wait until the pull request that fixes them merges.

    People or alerts
  2. A model drafts one test

    It reads the incident, the fix and your nearby tests, then writes a single regression test in your repository's style. Until it is proven, it is only a draft.

    Model, never trusted alone
  3. Git history decides

    In a fresh sandbox the test must fail three times on the commit before the fix and pass three times on the fix. Anything else is rejected and redrafted, up to three times.

    Real test runs
  4. It guards every pull request

    The proven test lands in your repository through a pull request. Every change that touches the same code runs it and gets one check: Recur, Safe, Inconclusive or Skipped.

    Real test runs

Proof, not opinion.

AI test generators write tests nobody checked against history. Review bots comment. Incident tools stop at the postmortem. Aftershock is the part that makes the lesson executable.

How Aftershock compares with other kinds of tools
What you getAftershockAI test generatorsCode review botsIncident platforms
Test is proven against git history (fails before the fix, passes on it) Yes No No No
Runs on every relevant pull request Yes Partly Yes No
Plain test file you keep if you leave Yes Yes No No
Finds the commit that introduced the bug Yes No No No
Signed evidence for audits Yes No No Partly

Named comparisons, with sources

What does a repeat incident cost you?

The same bug coming back is the most avoidable outage there is. Here is the cost, using your own numbers.

Repeat incidents a year7.2115 engineer hours, about $13,824, spent on bugs you already fixed once.Every default is a placeholder. Put in your own numbers; nothing here is sent anywhere.

Fits the tools you already have.

Incidents come from

  • GitHub issues
  • Postmortems
  • Sentry
  • PagerDuty
  • incident.io
  • Linear
  • Rootly
  • Opsgenie
  • Datadog
  • Jira
  • Generic webhook

Tests run with

  • pytest · Python
  • vitest · TypeScript
  • jest · TypeScript
  • go test · Go (Labs)
  • JUnit (Maven or Gradle) · Java (Labs)
  • RSpec · Ruby (Labs)
  • Playwright · TypeScript (Labs)

Your coding agents remember the outage, too.

Claude Code, Cursor and Copilot can ask Aftershock which past incidents a change touches before they commit, over MCP. Lessons also sync into AGENTS.md.

terminal
$ claude mcp add --transport http aftershock https://aftershock-mk.vercel.app/api/mcp \    --header "Authorization: Bearer as_live_…" > check_change files=["app/services/payment_service.py"] This change touches code from 1 past incident. Keep this behaviour:- INC-12 Payment retry charged a customer twice:  one order, one charge; the retry is rejected with 400  (test tests/aftershock/test_inc_12_payment_retry.py)
Set up an agent
aftershock-evidence-INC-12.json
{  "payload": {    "schema": "aftershock.evidence/v1",    "incident": { "key": "INC-12", "status": "proven" },    "proof": [{ "beforeTheFix": { "sha": "3f2a1c9…", "outcomes": ["failed","failed","failed"] },                "onTheFix":     { "sha": "aa86f56…", "outcomes": ["passed","passed","passed"] } }],    "guard": [{ "prNumber": 214, "verdict": "recur", "overridden": false }]  },  "signature": { "alg": "EdDSA", "kid": "as-3e330433736fb385", "canonicalization": "RFC8785", "value": "…" }}

Evidence an auditor can verify.

Each incident exports a bundle signed with Ed25519: the proof runs with their commits, every pull request the test guarded, and any overrides. SOC 2 and DORA both ask for exactly this.

See what is in a bundle

Before you install

Does a model decide whether a test is good?

No. A model only drafts. A test is admitted when it fails on the commit before the fix and passes on the fix, three runs each. Mixed results are never green.

Where does our code run?

In a fresh Firecracker microVM per run with no secrets and no network while tests run, or on your own GitHub Actions runners if you prefer.

What languages work?

Python (pytest) and TypeScript (vitest, jest) today, with Go, Java and Kotlin (JUnit), Ruby (RSpec) and Playwright browser tests in Labs.

What if we stop using Aftershock?

Your proven tests are ordinary files in your repository. They keep running in your CI without us.

Your last incident is the best place to start.