Fissure

Live demo: https://fissure-xi.vercel.app Code: https://github.com/agulam-coco/fissure

Finding car defects in owner complaints, months before the recall.


Inspiration

In 2015 Ford recalled the Fusion because the electric power steering could fail and leave you fighting the wheel. By the day that recall was filed, 1,215 drivers had already reported it to NHTSA. Two years before the recall, 192 had.

The warning was sitting in a public database the whole time. I wanted to know why nobody caught it, and whether a machine reading the complaints could have.


What it does

Fissure reads NHTSA owner complaint narratives and groups them by what drivers actually describe, ignoring the official category entirely. When a group starts growing, it flags it.

Replaying history month by month on the Ford Fusion, it flags the steering defect in July 2012. The recall came in September 2015. That is 38.3 months of warning, using only complaints that had actually been filed at the time.

On the front end, you describe a problem in plain English ("the wheel got really heavy while I was turning") and IBM Granite on watsonx matches it to a known defect pattern and explains why it matched. The match erupts out of a volcano, and the orbiting bubbles are the official categories that one defect got scattered across.


How I built it

The offline half is Python. Complaint narratives go through TF-IDF on 1 and 2 grams, get reduced with truncated SVD to 100 dimensions, L2 normalized so KMeans behaves like cosine clustering, and then clustered. No component codes, no recall data, nothing supervised goes in. Then I backtest each cluster against the real recall date using DATEA, the date the complaint was filed, not the date the failure happened, because an early warning system can only act on what has actually been reported.

The live half is Next.js 16 and React 19 on Vercel. A server route builds a prompt from the validated defect patterns and asks IBM Granite (granite-4-h-small) on watsonx.ai to match the driver's description, returning structured JSON with a confidence and a one-sentence reason. The server validates that the returned id is a real pattern rather than trusting the model, and a local keyword matcher takes over if watsonx is slow or down, so the demo cannot break.

The volcano is hand-drawn SVG for the scenery and Canvas 2D for the particle plume, sharing one coordinate system so the eruption always lands on the crater at any screen size.


Challenges I ran into

Standard text cleaning deletes the word "not." On the Honda Civic airbag recall I got a 98.2% pure cluster and almost celebrated, until I read the top terms: "did not deploy." I had captured airbag non-deployment, the exact opposite of the inflator rupture being recalled. Default English stopword lists strip "not," "no" and "never," which silently inverts the meaning of a complaint. I now keep those words.


Accomplishments that I'm proud of

I tested six real recalls and only two held up. The other four are in the app with the numbers that killed them.

The one I'm proudest of is a failure. On the GM ignition switch recall, my alert rule would have fired 97 months early. I don't count it, because that cluster was only 1.6x better than picking complaints at random, and an alert you cannot attribute to a specific failure is a coincidence with a good date on it. It would have been the most impressive number in the project and I threw it out.


What I learned

The categories are mostly fine. NHTSA labels about 96% of these complaints correctly, which surprised me, since I started out assuming the labels were the problem. The real issue is that a category tells you which part, and a recall is about one specific failure of that part. My two validated recalls are both "steering," both correctly labeled, and completely different defects.

I also learned that you can only know the labels are 96% accurate by reading the text first. The label cannot grade itself.


What's next for Fissure

Run it on live NHTSA data every month instead of a static snapshot. Push into component families beyond steering, which is the gap the Cobalt exposed. And weight clusters by severity, since the complaint file already carries crash, injury, fire and death flags that I'm not using yet.


Built with

ibm-watsonx, ibm-granite, ibm-cloud, python, scikit-learn, pandas, numpy, machine-learning, nlp, unsupervised-learning, clustering, k-means, tf-idf, next.js, react, typescript, tailwindcss, vercel, node.js, rest-api, svg, canvas, nhtsa, open-data

"Try it out" links

Built With

Share this project:

Updates

Submission history