Taleshaunt, a post-mortem narrator
  1. spawn

    python -m haunt.demo_crash

  2. stderr

    Lighting the candles…

  3. stderr

    The room grows cold.

  4. panic

    ZeroDivisionError: division by zero

  5. render

    GHOST_REPORT.md

  6. 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.

install  — installs the 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.

  1. The Crash Site

    Where it happened: the command, the working directory, the commit you were on, and how long the process survived.

  2. 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.

  3. Prime Suspects

    The traceback frame that raised, the files you changed but never committed, and the Python and platform fingerprint.

  4. The Epitaph

    The one line that mattered, quoted back at you, and the sentence that belongs on the tombstone.

  5. 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 fails
  • haunt run --no-narrate -- ./binrecord the evidence without waking the model
  • haunt analyze crash.lognarrate a log you already have
  • haunt demobury the bundled program

Pass the command after --, or the flags of the wrapped program are read as haunt’s own.

Read the full manual — every flag, field and failure mode →