This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine
Programs die on my machine constantly and nobody notices. A segfault scrolls off the screen. A kill goes through. The only record is a number in $? that I will never look at again.
So I built grave, a small wrapper that treats those deaths as worth remembering.
grave ./myprogram
If the program exits cleanly, grave says nothing. If it dies, a local Gemma model writes its epitaph using only the facts of its death. Then you can hold a séance:
grave seance "who killed you?"
The most recent ghost answers from its own record. If the record doesn't say, it's supposed to admit it doesn't remember.
It's 99 lines of C++ and does one thing.
VIDEO_LINK_HERE
The video is recorded with the Wi-Fi off. First a segfault, then the séance:
$ ./grave ./crash
OUTPUT_1_HERE
$ ./grave seance "who killed you?"
OUTPUT_2_HERE
REPO_URL_HERE
The wrapper forks and execs your command, pipes its stderr to keep its last words, and calls wait4 for the exit status, the killing signal, and peak memory. A clean exit is silent. Anything else becomes a fact sheet: name, seconds alive, exit code or signal, peak memory, last words.
The fact sheet goes to Gemma (gemma3:4b) through Ollama with one rule:
Write a 3 sentence epitaph for a dead process. Use ONLY these facts. Do not invent a cause of death or any detail not listed.
One design decision came from testing. My first version stored the epitaphs in the same file the séance reads. That means a hallucinated epitaph would later be quoted back as fact. So the graveyard (~/.graveyard) holds facts only, epitaphs live in a separate file, and the séance only ever sees the facts of the most recent death.
Limits. A 4B model can still embellish no matter what the prompt says. That's why the raw facts sit next to every epitaph: you can always check what the model was told against what it wrote. grave also only sees what it launches, not every death on the system. And a process's stderr goes straight into the prompt, so a hostile program could write instructions into its own last words. The exit status prints first and the epitaph follows, because the model takes a few seconds on a laptop.
A dead process's last words are rarely harmless. They carry file paths, usernames, hostnames, and half a stack trace. I wasn't willing to send that to a server I don't control just to get a sad sentence back.
With an open-weight model, the demo runs with the Wi-Fi off, which the video shows. Nothing leaves the machine, and it costs nothing per death. Swapping models is one environment variable, GRAVE_MODEL, so when a better small model ships, nothing in the code changes.
A closed API could write the same epitaph. It couldn't do it offline, privately, and for free.
The first thing I buried was a program I wrote to crash on purpose. It allocated 50 MB, touched every page, then dereferenced a null pointer. I knew exactly how it would die, and I still read the epitaph like it was news.
This is what came back:
EPITAPH_HERE
Then I asked the dead process who killed it. It could only answer from its record, and the record says one thing: signal 11, a segmentation fault. No murderer, just a pointer that pointed nowhere.
That's what stuck with me. Every crash I've ever had was this specific and this unremarkable, and I scrolled past all of them. Something should at least say a few words.
Best Use of Gemma