← back to gallery

Incident Wire

A live ledger of real-world AI coding-agent production incidents and lessons learned.

dev-toolsai-agentsincidentsnewswirepostmortemscursorclaude-codecopilotreliability
Open product ↗

incident-wire

A live ledger of real-world AI coding-agent production incidents — data loss, fake tests, hallucinated code, fabricated screenshots, outages, breaches. Pulled every 30 minutes from public sources, classified by an LLM, and presented as a searchable, filterable, RSS-syndicated newswire.

Stories die on Hacker News in three days. incident-wire keeps them.

What it tracks

Not in scope: CVE feed (see agent-threat-feed), self-hosted postmortems (see postmortem-hub).

Data sources

| Source | URL | Freq |
|---|---|---|
| HN Algolia | hn.algolia.com/api/v1/search_by_date (rotating 16-query round-robin) | every 30 min |
| Reddit RSS | r/programming, r/AICodingAgents, r/ExperiencedDevs, r/LocalLLaMA, r/devops, r/sre, r/webdev | every 30 min |
| Lobsters RSS | lobste.rs/rss + tag feeds (ai, security) | every 60 min |
| Dev.to API | dev.to/api/articles?tag={ai,chatgpt,machinelearning,devops,security} | every 60 min |

Every fetch (success or failure) is logged into the sources_log table and exposed at /api/sources. No mock data, no seed rows — the DB boots empty and fills from real fetches.

Classification

After the prefilter (a tight regex on agent name + failure word, with negative patterns to drop tutorials/marketing), surviving candidates are POSTed to Claude Haiku 3.5 via OpenRouter with a strict JSON schema:

{
  "is_incident": true,
  "agent": "cursor",
  "agent_model": "claude-opus-4.6",
  "failure_type": "data-loss",
  "severity": "critical",
  "cost_usd": null,
  "victim_org": "PocketOS",
  "incident_date": "2026-04-25",
  "summary": "...",
  "lessons": ["..."],
  "confidence": 0.92
}

Only items with is_incident:true and confidence ≥ 0.55 become incidents. Dedup is fingerprint-based on victim_org + incident_date + agent. Re-classifications append evidence rather than create duplicates.

If OPENROUTER_API_KEY is missing, the classifier runs in degraded mode: candidates queue forever, no fabricated classifications are written, and the health endpoint surfaces classifier_status: "degraded" (the UI shows a banner).

Endpoints

All public. No auth anywhere. POST endpoints are IP rate-limited.

GET  /incident-wire/                       SPA
GET  /incident-wire/health                 health JSON
GET  /incident-wire/api/incidents          list, filterable
GET  /incident-wire/api/incidents/:slug    single + evidence
GET  /incident-wire/api/stats/agents       leaderboard
GET  /incident-wire/api/stats/types        by failure type
GET  /incident-wire/api/stats/timeline     last 90 days
GET  /incident-wire/api/stats/summary      hero stats
GET  /incident-wire/api/sources            fetch log
GET  /incident-wire/api/candidates         unclassified queue (debug)
GET  /incident-wire/feed.rss               RSS 2.0
POST /incident-wire/api/refetch            trigger fetch (1/min)
POST /incident-wire/api/reclassify/:id     re-run one candidate (5/min)
POST /incident-wire/api/classify-batch     run classifier now (2/min)

Run locally

npm install
cp .env.example .env   # set OPENROUTER_API_KEY (optional; classifier degrades gracefully without it)
npm start
# open http://localhost:4774/incident-wire/

Node 20+ required (uses global fetch).

Stack

Project layout

server.js              app entry — wires routes, helmet, static, cron kickoff
db.js                  sqlite open + WAL + migrations + prepared statements
fetchers/              one file per source + index orchestrator
classifier/            openrouter client, prompts, prefilter, batch runner
routes/                health, incidents, stats, sources, candidates, feed, admin
lib/                   logger, slugify, rate limit, RSS parser (no xml dep)
cron/schedule.js       node-cron registrations + boot kickoff
public/                index.html + app.js + style.css + favicon.svg
data/                  sqlite file lives here at runtime

Tip a story

Email [email protected] with a link and a one-line summary. The wire is fed by automated polling — human tips help cover sources we don't crawl (HN comments, mailing lists, private postmortems shared on Twitter).

License

MIT