-
spawn
python -m haunt.demo_crash
-
stderr
Lighting the candles…
-
stderr
The room grows cold.
-
panic
ZeroDivisionError: division by zero
-
render
GHOST_REPORT.md
- from a single traced run
Written by gemma2:2b in 3.6 seconds, on the machine that crashed.
Tales, a post-mortem narrator. Sample narration: The crash site was a dusty attic, illuminated by a single flickering bulb.
That is not a prompt. haunt ran a Python program, watched it die, and collected
the traceback, the git diff, the exit code and the last lines of stderr. Then it handed the
evidence to a local open-weight model and asked it for a post-mortem in the form of a ghost
story. The model wrote the eulogy. Nothing left the machine.
haunt command
$ git clone https://github.com/Ewan-Dkhar/Tales $ cd Tales && uv venv && uv pip install -e . $ ollama pull gemma2:2b $ .venv/bin/haunt demo # Tales installs as haunt. Needs Python 3.11+ and Ollama.
the ghost may invent prose,
not facts
The story is allowed to be a story. The evidence is not.
Recorded by haunt
"command": ["python", "-m", "haunt.demo_crash"], "exit_code": 1, "signal": null, "duration_s": 0.022, "stderr_tail": "Lighting the candles... The room grows cold. Traceback (most recent call last): File "demo_crash.py", line 12 return 1 / candles ZeroDivisionError: division by zero", "git": { "branch": "main", "uncommitted": [ "M haunt/cli.py" ] }, "env": { "python": "3.12.5" }
Abridged for width. The full block is appended to every report.
Narrated by the model
“The process, a haunted demo named ‘haunt.demo_crash’, met its end with a chilling ‘ZeroDivisionError: division by zero.’ This was no mere technical failure; it was a manifestation of the program’s own chillingly poetic demise.”
Verbatim from GHOST_REPORT.md.
The prompt forbids invented facts and the report keeps the raw evidence as an appendix, so the prose can always be audited against the JSON. The model is not asked to diagnose anything it cannot see: it gets the traceback, the files you changed and never committed, the branch, the exit code, and the last hundred lines the process managed to say.
Everything runs on the local Ollama daemon. There is no API key, no account, and no network call to anyone else — the crash data goes into a prompt on your own disk.
five sections,
in the order they are needed
Every report follows the same five movements.
-
The Crash Site
Where it happened: the command, the working directory, the commit you were on, and how long the process survived.
-
Last Words
What the process printed on the way down — the exit code, any signal that killed it, and the tail of stdout and stderr.
-
Prime Suspects
The traceback frame that raised, the files you changed but never committed, and the Python and platform fingerprint.
-
The Epitaph
The one line that mattered, quoted back at you, and the sentence that belongs on the tombstone.
-
How to Prevent the Haunting
One concrete change, argued from the evidence rather than from the model’s imagination.
the fine print,
which is the interesting part
What it will and will not do.
| Runs offline | By default nothing leaves the machine. A sink has to be configured explicitly before any crash data is sent anywhere. |
|---|---|
| Model swappable | Gemma 2 is the default because it runs on a laptop. --model takes any Ollama tag, or set HAUNT_MODEL. |
| Native crashes | A segfault or an OOM kill leaves no traceback. haunt still writes the report from the exit code, the signal, and whatever reached stderr. |
| The report | GHOST_REPORT.md holds the story, and below it the raw evidence as JSON, so a claim in the prose can be checked against what was recorded. It is written whenever an output path is set — skip the model and the evidence still ships. |
| A clean exit | If the wrapped process exits 0, haunt stays quiet: no story, no report. There is no death to narrate, and inventing one is the failure mode this tool exists to avoid. |
| Existing logs | haunt analyze crash.log narrates a death that already happened, from a file you kept. |
| Traced | One haunt.pipeline transaction per run, with spans for capture, gather, narrate and render — including the HTTP call to Ollama. |
four commands
Point it at something that dies.
haunt run -- python app.pywrap a process, narrate it if it failshaunt run --no-narrate -- ./binrecord the evidence without waking the modelhaunt analyze crash.lognarrate a log you already havehaunt demobury the bundled program
Pass the command after --, or the flags of the wrapped program are read as haunt’s own.