# grave: I Gave Every Dead Process on My Machine an Epitaph

> Source: <https://dev.to/musicbeatz20072023/grave-i-gave-every-dead-process-on-my-machine-an-epitaph-l95>
> Published: 2026-10-29 14:47:16+00:00

*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine](https://dev.to/challenges/hacktoberfest-week4-2026-10-26)*

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
