DEV Community

Cover image for git-ofrenda: an altar for the code your repo buried, with epitaphs by Gemma on a plain CPU 🕯️
ANIRUDDHA  ADAK
ANIRUDDHA ADAK Subscriber

Posted on

git-ofrenda: an altar for the code your repo buried, with epitaphs by Gemma on a plain CPU 🕯️

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

What I Built

git-ofrenda 🕯️🌼 builds an ofrenda for the code your repo buried.

Every repository that lives long enough has a graveyard. Files that handled real traffic for years, got touched by a hundred commits and half a dozen people, and then vanished in a commit called "Removed old cruft". git log still remembers them. Nobody visits.

git-ofrenda digs them up:

  1. It walks your git history and finds every file that was deleted and never came back.
  2. It collects the facts of each one's life from git: when it was born and who added it, when it died and who removed it, the commit message that killed it, how many commits it lived through, who tended it, and its last lines of code.
  3. A local open-weight model (Gemma 3, through Ollama or llama.cpp) writes each one an epitaph and a short remembrance.
  4. It builds an altar: one static page with a candlelit headstone per file.
  5. And if you want to ask a dead file something, ofrenda seance lets you talk to it. The ghost only knows its own record.

That's my reading of the theme from both sides: Halloween gets the séance, and Día de los Muertos gets the part I care about more, remembering the work that came before. Open source runs on code that someone wrote, maintained, and eventually retired. Those files deserve better than a line in git log --diff-filter=D.

It's a single Python file with no dependencies beyond the standard library, and it ships with a SKILL.md, so a coding agent can run it for you too. I build agent skills, so that was the natural shape.

Demo

The altar for Express.js: https://aniruddhaadak80.github.io/API_projects/git-ofrenda/

I pointed it at the Express repository: 6,183 commits, and 502 files deleted and never revived. The altar has 12 headstones, the .js files that lived through the most commits. A lot of them are from Express's 2009-2011 era: lib/express/core.js, request.js, view.js, a plugins/ folder, a Collection class, and tests for a Jade view engine.

The Express altar

Each headstone keeps the model's words and git's facts apart. The italic epitaph and the short paragraph are Gemma's. The dates, commit count, number of keepers, cause of death and last words are printed straight from git.

One headstone with its last words opened

Here's the full build and a séance, as it ran:

Build log and séance in the terminal

Code

GitHub logo aniruddhaadak80 / API_projects

Deployment URL of API_projects

git-ofrenda lives in the git-ofrenda/ folder (MIT).

ofrenda.py        the CLI: list, build, render, seance (stdlib only)
SKILL.md          agent skill: when to use it, how to read the output
test_ofrenda.py   11 unit tests
index.html        the live Express altar
express/          raw souls.json, OFRENDA.md, build log, séance transcript
Enter fullscreen mode Exit fullscreen mode

Try it on your own repo:

ollama pull gemma3:4b
python3 ofrenda.py list  ~/src/your-repo                      # what did it bury?
python3 ofrenda.py build ~/src/your-repo --model gemma3:4b    # build the altar
python3 ofrenda.py seance ~/src/your-repo path/to/dead/file.js --model gemma3:4b
Enter fullscreen mode Exit fullscreen mode

It also works with llama.cpp's llama-server, LM Studio or vLLM: pass --url http://localhost:8080/v1. With no model at all, --no-model still builds a facts-only altar.

How I Built It

The open pieces

  • Gemma 3 4B Instruct, an open-weight model, as a 4-bit GGUF (ggml-org/gemma-3-4b-it-GGUF, Q4_K_M, 2.49 GB).
  • llama.cpp (llama-server, build b11913) for local inference on plain CPU. The same code also talks to Ollama's native API.
  • git itself, which is open source and is the real archivist here.

Git does the remembering, Gemma does the mourning

The rule I followed: anything that can be a fact comes from git, and the model only writes words.

  • git log --diff-filter=D finds deletions. Renames aren't deaths (git's rename detection handles that). Files deleted and later re-added are alive, so they're skipped. Lockfiles, images and minified bundles are ignored.
  • For each file it reads the first commit that added it, every commit that touched it, and its content one commit before it died (git show <death>^:path). The last 12 non-blank lines become its "last words".
  • Files are ranked by how many commits they lived through, with at most two per burial commit, so one big cleanup can't take every headstone.

Keeping a small model honest

The prompt gives Gemma the facts block and asks for exactly two lines, EPITAPH: and REMEMBRANCE:. Then code checks the answer:

  • Wrong shape (a list of "options", a missing line, a ramble) → retry.
  • Any number that isn't in the file's facts → reject and retry. A model that says a file "fixed 47 bugs" when git never said so doesn't get a headstone.
  • Three misses → a plain factual template, and the card says written by template.

My first prompt got me twelve near-identical epitaphs about "silent echoes" and "digital dust". Telling the model to play on something particular to this file (its name, its last lines, the words of its cause of death) and banning those stock phrases helped a lot. Gemma started quoting the code: view.js got "'Return str;' – A final, insistent plea.", and the view plugin, whose final lines hand null to a callback, got "'Null' was its last word, a clean, decisive end."

The séance

seance gives the model the file's record as its whole world and tells it to say "the veil hides it" for anything the record doesn't answer. I asked lib/express/core.js why it was let go:

Oh, my dear, it was a rather abrupt farewell, wasn't it? Tj Holowaychuk simply deemed me "old cruft" – a terribly blunt assessment, if you ask me. I suppose I did spend a good 200 days diligently handling requests, though my last lines were quite straightforward: if (typeof (body = (new Router(request)).route()) === 'string') request.respond(200, body). The veil hides the specifics of why they chose that particular moment, but I don't truly regret my work, it was a simple, necessary task.

Every fact in that answer checks out against git: the commit message, who removed it, the 200 days, and that line really is in its final version.

What I measured

All on a machine with 2 ARM CPU cores, 7 GB RAM and no GPU:

Gemma 3 4B (Q4_K_M) Gemma 3 1B (Q4_K_M)
Altar of 12 headstones 317 s (≈26 s per file) 118 s
Fell back to the template 0 of 12 2 of 12
Generation speed ≈8 tokens/s ≈27 tokens/s
One séance answer 34 s not tested

The 1B model is three times faster, but its epitaphs were generic, so 4B is what I'd use. Five minutes for an altar is fine for something you run once per repo.

Where it still gets things wrong

The number guard catches invented statistics, not invented meaning. On Express, Gemma 4B called test/response.test.js a "small file" that "served only to declare 'hey'". It was 769 lines with 103 commits; it just happened to end with a tiny res.send('<p>hey</p>') test. That's exactly why the dates and counts on every headstone come from git and the poetry is labelled as the model's.

Tests (python3 -m unittest -v test_ofrenda.py, 11 passing) build a throwaway repo with a rename, a resurrection and a binary file, and check the archaeology, the parser, the number guard, the retry-then-template path against a fake OpenAI-style server and a fake Ollama server, and an unreachable server.

Why Does Open Innovation Matter?

Your git history is private, and this keeps it that way. A repo's history is full of names, internal commit messages and old code that was removed for a reason. An open-weight model running on your own machine means git-ofrenda can read all of it without uploading anything. I went one step further: the HTTP client ignores system proxy settings, so requests go straight to the localhost URL you give it.

It works with the lights off. After the model is downloaded once, building an altar or holding a séance needs no internet and no API key.

It runs on whatever you have. Gemma 3 is open-weight and comes in sizes from 1B up, and llama.cpp and Ollama run it on a laptop CPU. I measured it on two CPU cores with no GPU. A closed API would have given me nicer prose and a bill per repo, and it would stop working the day the service changes its terms.

It fits the theme. This is a tool for honouring code that open-source maintainers wrote and retired. It would feel wrong if remembering their work depended on a service that might disappear too. The model, the runtime, git, and git-ofrenda itself are all open, so the altar can outlive any one of them.

The Spirit of It

The moment it worked for me was opening lib/express/core.js on the altar: born November 2009, 222 commits, 6 keepers, gone in June 2010 with "Removed old cruft", and its last lines still there, wiring request.trigger('request') to a Router. That's one of the earliest pieces of a framework that a huge part of the Node.js world was built on, and most people using Express today have never seen it.

git-ofrenda doesn't make that code come back. It just lights a candle for it.

Prize Categories

  • Best Use of Gemma: Gemma 3 4B Instruct (open weights, GGUF via llama.cpp) writes every epitaph and speaks at every séance. Gemma 3 1B was measured as the fast option.

Top comments (0)