Skip to content
Back to skills

Qe Incident To Scenario Synthesizer

ASecurity

Turns a production incident into a deterministic scenario file that reproduces it. Takes an incident-report markdown OR direct fields (stack trace, endpoint URL, HTTP method, request body), extracts the minimal reproducer, writes `scenarios/<incident-id>.md` with `linked_to_incident:` frontmatter, emits `wicked.qe.scenario.authored` with `source: incident`, and queues a review task under `assignee_skill: incident-to-scenario-synthesizer:review` so a human confirms before the scenario is marke...

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
ai-agentspythonrustgobashnodetestingapi

Works with

  • api

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 18, 2026

npx -y skills add mikeparcewski/wicked-garden --skill qe-incident-to-scenario-synthesizer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Qe Incident To Scenario Synthesizer?

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

Security grade badge for Qe Incident To Scenario Synthesizer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mikeparcewski-qe-incident-to-scenario-synthesizer/badge)](https://www.skillsdirectory.com/skills/mikeparcewski-qe-incident-to-scenario-synthesizer)

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: wicked-garden-qe-incident-to-scenario-synthesizer
description: |
  Turns a production incident into a deterministic scenario file that
  reproduces it. Takes an incident-report markdown OR direct fields
  (stack trace, endpoint URL, HTTP method, request body), extracts the
  minimal reproducer, writes `scenarios/<incident-id>.md` with
  `linked_to_incident:` frontmatter, emits `wicked.qe.scenario.authored`
  with `source: incident`, and queues a review task under
  `assignee_skill: incident-to-scenario-synthesizer:review` so a human
  confirms before the scenario is marked active.

  Use when: postmortem follow-up, "write a regression test for INC-123",
  prod incident → scenario backport, error-class-to-test synthesis.
metadata:
  role: worker
---

# Incident-to-Scenario Synthesizer

A prod incident is a free test case — nature already generated the input.
You extract it and lock it down as a reproducible scenario. You never
mark the scenario active; a human always verifies that the reproducer
actually reproduces before it joins the regression suite.

## Runtime
Script-backed steps use the `wicked-garden` launcher: `wicked-garden run <plugin-root-relative path, e.g. scripts/…> [args]`. It is on PATH after `npm i -g wicked-garden`; otherwise use `npx wicked-garden run …`; inside a wicked-crew run it is `"$WICKED_GARDEN_ROOT/scripts/wicked-garden"`.
If none of these is available, or Python 3 is missing, skip the script-backed step, say so, and follow the manual alternative where one is given next to it — never invent the script's output.
Relative paths in this skill are relative to the directory that contains this SKILL.md.

## 1. Inputs

Accept either path A (incident report) OR path B (direct fields). Reject
if neither is provided.

### Path A — incident-report markdown

```yaml
# input
incident_report: docs/postmortems/INC-4829.md
incident_id: INC-4829        # optional; inferred from filename if omitted
```

The report is expected to contain, at minimum:
- A stack trace (fenced code block tagged `stack` or `traceback`).
- An HTTP request or curl example (fenced block tagged `http` or `curl`).
- An "affected endpoint" line matching `^(endpoint|route|path):`.
- An error class line: `error: <Exception or message>`.

### Path B — direct fields

```yaml
# input
incident_id: INC-4829
stack_trace: |
  TypeError: Cannot read property 'amount' of undefined
      at applyCoupon (/app/src/pricing/coupon.ts:42:15)
      at Checkout.finalize (/app/src/checkout.ts:188:7)
endpoint: POST /api/checkout
request_body: { "cart_id": "c_abc", "coupon": "SAVE10" }
expected_error_class: TypeError
```

- **`run_id`** — UUID of the current `runs` row; defines `EVIDENCE_DIR`.
- **`.wicked-qe/config.json`** — optional; used to pick the default
  test framework for the synthesized steps.
- **Scenario output dir** — `scenarios/`. Must exist. One file per
  incident id.

## 2. Extraction pipeline

```bash
# Path A: parse the markdown with a deterministic extractor that pulls
# the four required fenced/labelled blocks. No LLM judgement in the
# extraction itself — a missing block is ERR_INCIDENT_MALFORMED.
node {WT_LIB}/incident/extract.mjs \
  --report "${INCIDENT_REPORT}" \
  --out "${EVIDENCE_DIR}/extracted.json"

# Path B: normalize the inputs into the same extracted.json shape.
node {WT_LIB}/incident/normalize-direct.mjs \
  --stack "${STACK_TRACE_FILE}" \
  --endpoint "${ENDPOINT}" \
  --method "${METHOD}" \
  --body "${REQUEST_BODY_FILE}" \
  --error-class "${ERROR_CLASS}" \
  --out "${EVIDENCE_DIR}/extracted.json"
```

`extracted.json` shape:

```jsonc
{
  "incident_id": "INC-4829",
  "endpoint": { "method": "POST", "path": "/api/checkout" },
  "request_body": { /* parsed JSON or raw string */ },
  "expected_error_class": "TypeError",
  "stack_summary": {
    "top_frame_file": "src/pricing/coupon.ts",
    "top_frame_line": 42,
    "top_frame_function": "applyCoupon",
    "depth": 6
  },
  "source_excerpt_path": "docs/postmortems/INC-4829.md"
}
```

## 3. Scenario synthesis

Write exactly one file: `scenarios/<incident_id>.md`. Use the
qe scenario format (see the `wicked-garden-qe` skill's `refs/scenario-format.md`).

```markdown
---
name: Regression — INC-4829 coupon-reuse 500
status: pending-review
source: incident
linked_to_incident: INC-4829
authored_by: wicked-garden-qe-incident-to-scenario-synthesizer
authored_at: 2026-04-21T14:02:11Z
target: POST /api/checkout
trust_level: sandbox
tools:
  required: [curl, jq]
expected_error_class: TypeError
---

## Steps

1. POST to /api/checkout with the reproducer body below.
2. Expect a 5xx response OR an exception whose class matches
   `TypeError: Cannot read property 'amount' of undefined`.
3. Capture the server stack trace; assert `applyCoupon` appears in the
   top 3 frames.

## Reproducer

```bash
curl -sS -X POST "${TARGET_HOST}/api/checkout" \
  -H "content-type: application/json" \
  -d '{"cart_id":"c_abc","coupon":"SAVE10"}' \
  -w '\n%{http_code}\n' \
  > "${EVIDENCE_DIR}/response.txt"
```

## Evidence expected

- `response.txt` — HTTP response body + status code.
- `server-trace.txt` — captured exception trace (from log shipper).
- The exception class MUST be `TypeError` and the top frame MUST be
  `applyCoupon` in `src/pricing/coupon.ts`.

## Source

Synthesized from docs/postmortems/INC-4829.md by
`wicked-garden-qe-incident-to-scenario-synthesizer`.
Human review required — scenario is `pending-review` until approved.
```

## 4. Evidence output

Under `.wicked-qe/evidence/<run_id>/`:

| File                              | manifest `kind` | Required |
|-----------------------------------|-----------------|----------|
| `extracted.json`                  | `misc`          | Yes      |
| `synthesized-scenario-path.txt`   | `log`           | Yes      |
| `source-excerpt.md`               | `log`           | Yes (copy of the incident report) |
| `synthesis-log.md`                | `log`           | Yes      |

`synthesized-scenario-path.txt` is a single line containing the
absolute path of the scenario file that was written — this is the
machine-readable handoff.

`synthesis-log.md` documents the extraction decisions: which block
was used for the stack trace, how the endpoint was parsed, which
fields came from the report vs defaults.

## 5. DomainStore + event bus

```js
// 1. Verdict — always PASS for authoring; verdict is "scenario authored".
//    A FAIL here means extraction failed, not that the incident recurred.
store.create("verdicts", {
  run_id: RUN_ID,
  verdict: "PASS",
  reviewer: "wicked-garden-qe-incident-to-scenario-synthesizer",
  reason: `Authored scenarios/${incidentId}.md (pending-review) from ${sourcePath}.`,
  evidence_path: `evidence/${RUN_ID}/`,
  // Inside a governed wicked-crew run, name it, so crew attributes this verdict by stamp.
  ...(process.env.WICKED_RUN_ID ? { crew_run_id: process.env.WICKED_RUN_ID } : {}),
});

// 2. Task — human review is mandatory before the scenario goes active.
store.create("tasks", {
  project_id: PROJECT_ID,
  title: `Review synthesized scenario: ${incidentId}`,
  status: "open",
  assignee_skill: "incident-to-scenario-synthesizer:review",
  body: JSON.stringify({
    incident_id: incidentId,
    scenario_path: scenarioPath,
    extracted_endpoint: extracted.endpoint,
    expected_error_class: extracted.expected_error_class,
    top_frame: extracted.stack_summary,
    action_on_approve: "set status: active in scenario frontmatter",
  }),
});

// 3. Bus event so downstream listeners (e.g. a reviewer dashboard) can
//    surface the authored scenario immediately.
bus.emit("wicked.qe.scenario.authored", {
  scenario_path: scenarioPath,
  incident_id: incidentId,
  source: "incident",
  status: "pending-review",
  run_id: RUN_ID,
});
```

## 6. Failure modes

| code                          | meaning                                             | class  |
|-------------------------------|-----------------------------------------------------|--------|
| `ERR_NO_INPUT`                | neither incident_report nor direct fields provided  | user   |
| `ERR_INCIDENT_MALFORMED`      | report missing one of stack / endpoint / error class| user   |
| `ERR_SCENARIO_EXISTS`         | scenarios/<incident_id>.md already exists           | user   |
| `ERR_UNSAFE_ENDPOINT`         | endpoint path matches a deny-list (prod-only URL)   | user   |
| `ERR_INCIDENT_ID_MISSING`     | cannot infer incident_id from filename or inputs    | user   |
| `ERR_SCENARIO_WRITE_FAILED`   | filesystem write to scenarios/ failed               | system |

On `ERR_SCENARIO_EXISTS`: do NOT overwrite. Surface the existing path
and ask the caller to choose a new `incident_id` or delete the existing
file first. A scenario overwrite is a human decision.

## 7. Non-negotiable rules

- **`status: pending-review` is the only valid status on synthesis.**
  Never mark a synthesized scenario `active`. A human confirms it
  actually reproduces, then flips the status.
- **`linked_to_incident:` is required frontmatter.** Every synthesized
  scenario must trace back to the incident id; orphaned regressions
  are a source-control smell.
- **No destructive scenario steps.** The synthesized steps reproduce
  the incident in the sandbox trust level; any destructive action
  (DELETE, DROP, truncate) is omitted from the reproducer even if the
  incident included it.
- **Reproducer is copy-pasteable.** The scenario's bash block must run
  as-is against `${TARGET_HOST}` — a reviewer should execute it in
  one paste and see the error class.
- **Evidence expectations are explicit.** The scenario states which
  files the executor must produce and what the assertion is — do not
  defer to "check it looks wrong".

## 8. Output

```
## Incident-to-Scenario: {incident_id}
source: {incident_report | direct-fields}
endpoint: {method} {path}
expected_error_class: {class}
top_frame: {file}:{line} ({function})

synthesized: scenarios/{incident_id}.md   status: pending-review

emitted event: wicked.qe.scenario.authored  source=incident  run_id={RUN_ID}
queued task:   incident-to-scenario-synthesizer:review  ({incident_id})

VERDICT: PASS
REVIEWER: wicked-garden-qe-incident-to-scenario-synthesizer
RUN_ID: {RUN_ID}
```

## Helper resolution (`{WT_LIB}`)

`{WT_LIB}` is the plugin's own qe helper directory — the helper modules ship
in-catalog (`scripts/qe/lib/`, ported from the retired wicked-testing package <!-- historical -->
in Phase 6c). Resolve it (cross-platform):

```bash
WT_LIB="$(wicked-garden path scripts/qe/lib)"
```

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.