# git-afterlife: I Held a Séance for the Original flask.py

> Source: <https://dev.to/paulopnun/git-afterlife-i-held-a-seance-for-the-original-flaskpy-36o5>
> Published: 2026-10-27 18:06:17+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)*

Every repository has a graveyard.

Somewhere in your `git log` there is a file that was born in a hopeful first commit, survived dozens of refactors, got blamed for bugs it didn't cause, and was finally deleted by someone typing a commit message like `cleanup`. Nobody lit a candle for it.

**git-afterlife** does. It is a small command-line tool that digs every deleted file out of a repository's history and builds them an altar: an *ofrenda*, like the ones families raise on Día de los Muertos so that the people they lost can find their way home for one night. Each file gets a place on the altar with:

Then there is the part that gave me chills the first time it worked: **`seance`**. You pick one dead file and talk to its ghost. It answers in first person, from its real code and history.

I pointed it at [Flask](https://github.com/pallets/flask), and the most famous ghost on the altar is `flask.py`: the original single-file Flask, born on 2010-04-06 in a commit called `Initial checkin of stuff that exists so far.`, changed in 104 commits in less than three months, and laid to rest on 2010-07-02 by a commit whose entire message was `out with the old`. That file is where a framework used by millions of people started. git-afterlife gave it a candle.

This is what happened when I asked it why it died:

**you ›** Why did they delete you?

**spirit ›** It was a simple deletion, a regrettable act. Justin Quick, in a commit message stating, "out with the old," removed me from the repository. My final moments were a consequence of that single commit – a forgotten relic, purged from the git history.

Some of the epitaphs Gemma wrote for the altar:

| The deceased | Epitaph | 
|---|---|
| `flask.py` | *"A microframework, purged with a dismissive 'out with the old.'"* | 
| `tests/flask_tests.py` | *"Served Flask tests faithfully, then vanished with a split."* | 
| `setup.py` | *"A simple setup, swiftly replaced by a modern plea."* | 
| `docs/deploying/fastcgi.rst` | *"It guided deployments, then vanished with a rewrite."* | 

The demo is built from scratch by GitHub Actions: a runner installs Ollama, pulls Gemma 3 4B, clones Flask, raises the altar and holds a séance with `flask.py`, then publishes everything to GitHub Pages.

The altar is a single self-contained HTML page: papel picado swaying at the top, a flickering candle on every grave, marigold petals falling, and tiers like a real ofrenda, with one soul at the top and wider rows below. The first three souls also sat for a short **séance**, and their conversation is shown on their card. Open any card's *portrait* to read the file's last source code.

Try it on your own repository in under a minute:

```
ollama pull gemma3:4b      # or gemma3:1b on an older laptop
git clone https://github.com/PauLopNun/git-afterlife.git
python3 git-afterlife/afterlife.py summon /path/to/your/repo     # writes altar.html
python3 git-afterlife/afterlife.py seance some/deleted/file.py /path/to/your/repo
```

Or run it on any public repository without installing anything: fork the repo and use **Actions → Raise the altar → Run workflow**, choosing the repository and the model.

Every repository has a graveyard: files born in a hopeful first commit, carried through dozens of refactors, and finally deleted with a one-line commit message Nobody remembers them.

**git-afterlife** digs them out of your git history and builds them an altar, an *ofrenda*
the altar families raise on Día de los Muertos to welcome back the ones they
lost. Each deleted file gets a place on the altar with its birth date, its death
date, the people who touched it, the commit that killed it, and an epitaph with
its last words, written by an **open-weight model running on your own machine**.

You can also hold a **séance** and talk to the ghost of a deleted file. It
remembers its real code, who created it, and why it was removed.

| `summon` | `seance` | 
|---|---|
| Builds a self-contained HTML altar with papel picado, flickering candles, falling marigold petals and recorded séances |  | 

The whole tool is one Python file that uses only the standard library, so there is nothing to `pip install`. MIT licensed.

**The open pieces:** [Gemma 3](https://ai.google.dev/gemma), Google's open-weight model, served by [Ollama](https://ollama.com). git-afterlife talks to Ollama's HTTP API on `localhost` with plain `urllib`. There is no SDK and no cloud endpoint anywhere in the code.

**1. Git archaeology.** `git log --diff-filter=D` lists every deletion in the history. Files that were later resurrected at `HEAD` are skipped, and so are binaries. To decide who gets a place on the altar, one more `git log --name-only` counts how many commits touched each path, so the longest-lived source files are raised first while lock files and configs are left to rest. For each soul:

`git log -- <path>` up to the death commit gives its birth, its lifespan in commits and its mourners,`git show <death>^:<path>` recovers its final source code,
**2. Grounding a small model.** My first run with Gemma 3 1B worked end to end, and it was bad. Every epitaph was some variation of *"lost in the digital dust, a silent plea"*, asking for another language with `--lang` was mostly ignored, and in the séance `flask.py` claimed it had been deleted because of *"a subtle bug"*. That was invented: the commit says `out with the old`.

Three changes fixed most of it:

`dateutil` only appeared in my example. Every ofrenda has one guest who talks too much.
**3. Recorded séances.** `summon --seances 3` interviews the first three souls with the same grounded prompt as the interactive séance, and prints the conversation on their card, so you can meet the ghosts without opening a terminal.

**4. Don't trust a ghost that won't stop talking.** Epitaphs are requested in Ollama's JSON mode (`"format": "json"`), but valid JSON is not the same as a sane answer. In one demo run, two epitaphs finished their sentence and then kept going: stray closing braces, a timestamp and a long run of nineteen-digit numbers, all inside a perfectly valid JSON string, printed on the altar. Now every answer has a token cap (`num_predict`), and any epitaph that is too long or contains braces or long digit runs is thrown away and asked for again. There is a test for exactly that ghost.

**5. The altar.** The HTML is generated with plain string templates and CSS animations: no JavaScript framework and no external requests, so the page works offline too.

**6. Tests and the demo pipeline.** The test suite builds a tiny real repository with births, deaths, a binary and a file that comes back to life, and swaps the model for a fake medium, so it checks every step in a fraction of a second without Ollama. It also caught a real bug while I was adding séances to the altar. GitHub Actions runs the tests on every push. The demo is built on demand, because model output changes on every run: the workflow installs Ollama on a standard `ubuntu-latest` runner, pulls Gemma 3 4B, runs everything on CPU, takes the screenshots in this post with headless Chrome, records the séance GIF with [VHS](https://github.com/charmbracelet/vhs) and deploys it all to GitHub Pages.

Because the thing git-afterlife reads is **your source code**, and source code is the one thing many of us are explicitly not allowed to paste into a cloud chatbot. The repositories with the richest graveyards, a ten-year-old monolith at work or a client project, are exactly the ones whose code must not leave the building. With an open-weight model on `localhost`, it doesn't. You can hold a séance for your company's legacy billing module without sending a single line of it anywhere.

It also means:

`--model` takes any Ollama model: 1B on an old laptop, 4B for nicer prose, or something much larger on a GPU. Nothing in the code is tied to one vendor.
The moment it landed was the last question of the séance. I asked the original `flask.py`, dead since July 2010, whether it had a message for the developers who use Flask today. It had answered every question with facts: Armin Ronacher created it, Justin Quick deleted it, it held `Request`, `Response` and `Session`. Then it turned its own cause of death back on us:

**spirit ›** I whisper a warning: remember the roots! My design, born of Armin's meticulous work, still echoes within Flask. Respect the foundations, the careful layering of Werkzeug and Jinja2. My premature demise serves as a reminder – progress must be thoughtful, not simply… "out with the old."

That is the whole idea of Día de los Muertos, said by a Python file. The people who are gone stay alive while someone remembers them. Our code works the same way: almost everything we write will be deleted one day, often by us. The work still mattered, because the thing that replaced it was built on top of it. Flask is in production all over the world today because a 1,573-line file existed for 87 days in 2010.

Run it on your oldest repository. You will probably find a file you had forgotten you wrote. Light it a candle.

*This post and the project were made with the help of AI coding tools. I reviewed, ran and tested everything that is described here.*
