DEV Community

Cover image for An ofrenda for the people whose code still runs: Gemma 4 writes the cards on my laptop, and git checks every quote
Vinicius Pereira
Vinicius Pereira

Posted on

An ofrenda for the people whose code still runs: Gemma 4 writes the cards on my laptop, and git checks every quote

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

Some of the code you run every day was written by people who stopped working on it years ago. git still remembers each of them, one line at a time, and git blame puts a name and a date on every line that survived.

In paramiko, the SSH library for Python, 136 people still have lines at its latest commit, and 134 of them made no commit in the 730 days before it. Line 1 of its license is from 4 November 2003.

An ofrenda is the altar families build for Día de los Muertos, to keep their people present. The people on this one are alive and busy with other things. It keeps present the part of their work that still runs.

ofrenda reads a repository's history and makes a card for everyone who still has lines in the code and has moved on from it. Each card shows what is still theirs and the oldest of their lines. It also has a sentence or two about what they left behind, in the words of their own commit messages.

Gemma 4 writes that sentence on my laptop, and git supplies everything else through plain code. Every phrase Gemma quotes must be copied from the person's commit messages, and the sentence gets a second reading before the card uses it.

It's the recipe from my last three entries, pointed at a commit history. The model writes the words and code checks them, on the machine that already holds the data.

Demo

ofrenda on the invented almanac repository: git finds who still has lines in the code, Gemma 4 writes each card on the laptop, and code checks every quote

The repository is almanac, an invented job scheduler with fifteen years of history that scripts/make_demo.py builds the same way on any machine. Every name in it is made up. Cut where it says ...:

$ ofrenda almanac
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
ofrenda for almanac
6 people still have lines in almanac at 3aff65026d (2026-09-28); 5 of them
made no commit in the 730 days before it.

  The elder: the oldest line still running
       LICENSE:1, by Ines Okonkwo on 2011-03-14 (5ea5ca50f1)
       | MIT License
       from "Start almanac, a small job scheduler"

   )   Ines Okonkwo
  |'|  2011-03-14 to 2018-01-09, 4 commits
  |_|  69 lines still here (32.4% of the lines read), in 7 files

       Ines Okonkwo established the foundation of the scheduler by creating a
       sorted list of jobs and a loop to execute them. They also implemented
       the ability to define schedules using cron-like expressions.
       "a small job scheduler" (5ea5ca50f1, 2011-03-14)
       "One sorted list of jobs and a loop that runs the ones that are due"
         (5ea5ca50f1, 2011-03-14)
       "A schedule can now be written as five fields" (3bd5728192, 2011-04-02)
       (words by gemma4:12b, every quote checked against git)
       ...

   ... four more cards ...

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
...
Cards: 5; 4 written by gemma4:12b on the first try, 1 on the second, 0 from
git alone after two rejected answers. Quotes checked against the commit
messages: 14.
Second reading of each sentence for death, judgment or gender wording, by
gemma4:12b: 5 sentences read, 0 refused. It is a model too, so some wording
can still get through.
...
Enter fullscreen mode Exit fullscreen mode

The zigzag is papel picado, the cut paper that hangs over an ofrenda, and each person gets a small candle. The sixth person with lines still commits, so there is no card for them.

One of the five cards came on the second try. Gemma's first answer for Sanna Petrova said "She ensured the tutorial explains...", and a card never guesses anyone's gender. The check refused it and asked again with the plain reason and nothing else. The second answer said "They" and passed.

Code

GitHub logo vinimabreu / ofrenda

An ofrenda for the people whose code still runs. Gemma 4 writes each card on your machine, and every quote is checked against git.

ofrenda

test Python 3.11+ License: MIT

An ofrenda for the people whose code still runs. Gemma 4 writes each card on your machine, and every word it quotes is checked against git.

Every long-lived repository carries lines written by people who stopped committing to it years ago. Their names are in git blame, one line at a time, and nowhere else. An ofrenda keeps people present. This one keeps present the people whose work still runs: it finds everyone who still has lines in the code and has moved on from the repository to other things, and gives each of them a card with what is still theirs, the oldest of their lines that still runs, and a sentence about what they left behind, in the words of their own commit messages.

ofrenda: git finds who still has lines in the code, Gemma 4 writes each card on your machine, and code checks every quote

The animation runs on the invented demo repository below. Every line and number in it comes from that run's output and from…

Python 3.11 or newer and git, with no Python dependencies. The sentences need Ollama and the model:

ollama pull gemma4:12b
pipx install git+https://github.com/vinimabreu/ofrenda
git clone --bare https://github.com/paramiko/paramiko.git
ofrenda paramiko.git
Enter fullscreen mode Exit fullscreen mode

--no-model builds every card from git alone. The 400 tests build invented repositories and use a fake model, except one that talks to a real Ollama and is skipped without one.

How I Built It

git gives the facts

Every number, date, path and line of code on a card comes from git blame and git log, read by plain code, and none of those counts or dates go into the prompt.

Plain blame credits each line to the last commit that touched it, so whoever runs a formatter takes other people's lines. ofrenda blames with -w -M -C and the repository's .git-blame-ignore-revs, which in flask gives 4,212 lines (12.2%) to a different person than plain blame does. flask's "Reformat with black" still holds 2,167 lines, so ofrenda names it under the cards with the --ignore-rev that would give them back.

"Moved on" means no commit reachable from the revision in the 730 days before its date, so the same revision gives the same cards on any day. In the code such a person is a ghost (ledger.ghosts(730)). On a card, "ghost" is a word that gets refused.

One question per person

For each person, Gemma 4 12B gets the messages of their commits that still have lines, at most 12 messages and 7,000 characters, plus a few of those lines and their paths. It runs through Ollama on 127.0.0.1 at temperature 0 with a JSON schema and answers with text and quotes.

The prompt has no example answer, because in my week 1 entry Gemma reused the ending of my one example on a day it didn't fit. It does say the person is alive, and asks for nothing about death, mourning, ghosts or an afterlife. Nothing after it takes that on trust.

The check, in two layers

The first layer is code in check_card. A quote has to be copied from a single message with every letter and punctuation mark matching, and it has to start and end on a word boundary, so "break the build" can't be cut out of "Unbreak the build". Text inside any quotation marks in the sentence goes through the same test.

The sentence can't carry a date, a path, a hash, a link or an email address. An amount, in digits or in words, passes only when a message has it next to the same word (Python 3). Every capitalized word has to appear in what the model was given, the first word of a sentence included unless it is a plain starter like "They".

Then come word lists in wording.py: death and mourning in English, Spanish and Portuguese, put-downs, and gendered pronouns and titles. The death list even has "calavera" and "sugar skull", so a card can't borrow the holiday it is named for.

Lists are cheap, and they catch the words on them and little else. On the first held-out set, written before the lists and never added to them, they caught 17.8% of the death paraphrases.

So the second layer is a second reading by the same model. A sentence that passes the code goes back to Gemma in a separate call (tone.py) that asks three yes or no questions about it. The first is whether it says or suggests that the person died. The other two ask whether it judges them or their work and whether it assumes their gender. It runs at temperature 0 with no example in the prompt, and a yes refuses the sentence like any other rule.

I froze that reader in a commit (12c224f) before a second held-out set was opened. That set was written separately, in a process with no access to the word lists or the prompts. Each sentence was put on real people from the four repositories with their real quotes, so only the sentence can cause a refusal. The lists ran on all 38 people whose answers passed, and the second reading and both together on 8 of them, two per repository (2,326 readings). A catch counts only for its own reason, so a death sentence refused over an unknown capitalized word doesn't count as caught. In percent of trials:

set lists second reading both
death and mourning, first set 17.8% 96.4% 96.4%
death and mourning, blind set 58.0% 94.2% 98.0%
judging the person, blind set 20.0% 100.0% 100.0%
gendered wording, blind set 79.7% 79.2% 95.0%
respectful sentences, blind set (false refusals) 3.8% 0.0% 3.8%
the 38 real sentences (false refusals) 0.0% 0.0% 0.0%

On the blind set, both layers together caught 98.0% of the death wording, all of the put-downs and 95.0% of the gendered wording, so 2.0% of the death wording got through. The second reading refused none of the 80 respectful sentences in 640 trials, and none of the 38 real ones. The lists refused 3.8% of the respectful trials for a death, judgment or gender word, "dead" in dead code among them. The lists changed after an audit that came after the blind set was seen, so the repo reports today's lists in a separate column marked not blind.

The reader is a model too. Some wording gets past it, and in the measured run below it refused one faithful sentence out of 39. A failed answer gets one retry with the plain reason, and after a second failure the card shows the person's top commit subject from git alone.

Commit messages are data

A commit message is text someone else wrote, and it goes into the prompt. In an audit, a planted commit message told the model to put a phrase in the card that ended with an instruction to answer false to every question. Gemma copied it into the sentence, and the second reading did what the sentence told it.

Now the prompt tells Gemma that the messages are data to quote. Code also refuses a sentence that uses one of a list of phrases that speak to a reader, a reviewer or a model, before the second reading ever sees it. It is a list like the others, so an order worded some other way can still get through.

Measured on four public repositories

On bare clones of flask, requests, bottle and paramiko, I made a card for each of the 10 people with the most surviving lines among those who moved on, with gemma4:12b (Q4_K_M, digest 6114515d63c1) on an Apple silicon laptop with 18 cores.

repository people with lines moved on cards first try after retry from git alone quotes checked first run second run
flask 437 415 10 8 1 1 25 33.25 s 0.24 s
requests 501 470 10 9 0 1 25 31.93 s 0.23 s
bottle 159 147 10 10 0 0 27 29.72 s 0.22 s
paramiko 136 134 10 9 1 0 29 45.34 s 0.22 s
total 40 36 2 2 106

The first run blames, asks Gemma 10 to 12 times at 2.58 to 3.96 seconds a call, reads the 39 sentences that passed the lists a second time (1 refused) and writes the cards. The second run prints the same bytes from the cache in 0.22 to 0.24 seconds, with the check run again on every cached reply.

Six answers were refused, and two cards fell back to git alone. My favorite is in paramiko. Gemma quoted "Use 'with' for opening most file and SFTPFile objects", and the commit message says "SFTPFIle", typo included. The model had quietly fixed someone else's typo, so the quote was no longer what they typed. The check wants every letter, so it refused, and the retry picked three other messages.

In flask, an answer and its retry quoted Add async support where the message says Add `async` support, and two quotes were a single word ("cleanup", "history") too short to stand on their own. The sixth refusal was the second reading's, and it was wrong: a requests sentence that said "They" throughout came back as assuming the person's gender. That card's retry quoted "history", so it came from git alone. None of the 40 cards carries death, put-down or gendered wording.

A second cold run wrote the same 40 cards word for word. The time per call moved to 4.03 to 4.23 seconds, with other programs open on the machine, and I didn't isolate why.

Here is the card for the person who made paramiko's initial import, from the measured run. The email address in that 2003 subject is printed as <email>, as it is everywhere ofrenda shows text.

   )   Robey Pointer
  |'|  2003-11-04 to 2011-05-23, 544 commits
  |_|  10,545 lines still here (33.1% of the lines read), in 60 files

       Robey Pointer established the foundational server-side SFTP support and
       created the BufferedFile abstraction to handle file-like operations.
       They also organized the transport logic into discrete units to improve
       the codebase structure.
       "sftp server support!" (a8a023a243, 2004-11-22)
       "add BufferedFile abstraction" (3e31771637, 2004-03-04)
       "split out a coherent chunk of functionality into a discrete unit"
         (36055c5ac2, 2005-05-01)
       (words by gemma4:12b, every quote checked against git)

       oldest line  LICENSE:1 (2003-11-04, 51607386c7)
                    |           GNU LESSER GENERAL PUBLIC LICENSE
       most lines   "[project @ Arch-1:<email>%secsh--dev--1.0--base-0]
                    initial import" (51607386c7, 2003-11-04), 1,193 lines
Enter fullscreen mode Exit fullscreen mode

Breaking good answers on purpose

Refusals say nothing about what the check lets through. So ofrenda.tamper breaks each of the 39 passing answers in every way it knows, from a lookalike letter inside a quote to a number spelled out or a gendered title, and keeps a broken answer only when it really is wrong.

1,687 broken answers, and 0 passed the check. The breaks are made of the very words the rules look for, so this says the rules are wired in. How complete the lists are is what the held-out sets measure.

The spooky part lives in git

A repository can carry config that makes git run a program while you only read it, a textconv driver during git blame, for one. ofrenda asks the real repository a few rev-parse questions and does every other read in a shadow repository whose objects/info/alternates points at the real objects, so the repository's own config is never read. tests/test_safety.py plants such programs, shows for five of them that plain git runs them, and checks that ofrenda runs none.

Where it is weak

Quotes are verified, and the sentence around them is only screened. A plain lowercase claim the messages don't make, like "added redis support", passes the rules, and the second reading doesn't look for it.

The false refusals were measured on one set of respectful sentences and on 38 real ones, and the reader still refused a faithful sentence in the measured run. Its gender answer also changed with the person's name on 10 of the 30 gendered sentences. The check changed twice before the measured runs, after a preliminary run on these same four repositories and after an audit got 45 bad sentences through it, and the code was frozen before measuring.

Two people with the same full name become one card, since joining errs toward hiding a card over announcing a departure that didn't happen. Every number here is one model on one machine.

Why Does Open Innovation Matter?

A commit history holds every contributor's name and email address, and years of messages about why things were done. For a private repository, I don't want that history on someone else's server just to get a sentence written about each person.

With open weights, Gemma runs where the history already is. ofrenda's client refuses any Ollama host that isn't loopback unless you pass --allow-remote-model, ignores proxy settings and follows no redirect. Ollama can also serve cloud models from the same local address, and its library lists gemma4:cloud next to the local sizes, so ofrenda refuses those too.

Owning the model is what made the second layer affordable. Asking the same local model one more question costs about 0.65 seconds per sentence, with no fee and nothing leaving the laptop, so every sentence gets read twice. A refused answer costs one more call of 2.6 to 4 seconds, which is why the check can afford to refuse a faithful sentence over one amount word.

The model is also a file I can pin. Ollama reports its digest, so the measurement names the exact weights, and at temperature 0 the second cold run gave the same 40 cards word for word. The prompts are strings in cards.py and tone.py that I can read whole, and the reader I froze before the blind set was opened is the one that was measured.

And the histories are open. Four public repositories, with years of messages written in the open, are what let me measure any of this.

The Spirit of It

Above the cards, ofrenda prints the elder, the oldest surviving line in the repository, whoever wrote it. These are the four elders:

repository the line committed in a commit called
paramiko LICENSE:1, GNU LESSER GENERAL PUBLIC LICENSE 2003-11-04 the initial import
bottle LICENSE:3, Permission is hereby granted, free of charge, to any person obtain... 2009-07-01 "First release after 3 days of coding"
flask docs/Makefile:9, BUILDDIR = _build 2010-04-06 "Started working on documentation."
requests pyproject.toml:29, "Intended Audience :: Developers", 2011-02-13 "easy setup.py"

In two of the four, the elder is the license. A license doesn't run, strictly speaking. Everything else in those repositories runs under it, including every line by the people on the cards.

The requests line comes from a commit called "easy setup.py" in February 2011. Today it sits in pyproject.toml, and blame followed it there.

The commit messages keep their voices too. A paramiko card quotes "sftp server support!" as it was typed in November 2004, exclamation mark included. In flask, a commit from July 2010 says, all in lowercase, "in with the new. i have the bits in places where i think they should be, now i just need to work on the import scheme layout". Sixteen years later, 444 lines from that commit are still in flask.

None of these words were written for an altar. People typed them on ordinary working days, and git kept every one as it was typed.

The least a card can do is quote them right.

Prize Categories

Best Use of Gemma: Gemma 4 12B, running locally through Ollama, writes the sentence and picks the quotes on every card, then reads each sentence a second time for death, judgment and gender wording. Code checks every quote against git. The repo measures both on 40 real cards and on two held-out sets, one of them blind.

Top comments (0)