This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine
What I Built
Every codebase has one: a line nobody will touch, under a comment that explains nothing.
await sleep(1300); // don't touch. ask ezra.
Ezra left in 2020.
Except Ezra never really left. Ezra is still in git blame, in the diff that added that line, and in a commit message nobody has opened since the day it was written. The reason is in the repository. It's just buried.
So I built git séance, a git subcommand that summons the ghost of whoever wrote a line of code and lets you ask them why. It runs entirely on my laptop through Google's open-weight Gemma 4 and Ollama, with no API key and no network.
$ git seance src/gateway.js:8 -q "The comment just says ask ezra. So: why 1300?"
🕯 The candles are lit.
🕯 A name is spoken: Ezra Thorne.
👻 The spirit of Ezra Thorne answers.
5 commits · 2018–2020 · departed 6 years ago
wrote src/gateway.js:8 on 2018-07-19: "fix: throttle FirstPacific calls"
👻 The cold always bites when you're not expecting it. I put the 1300ms there to
respect FirstPacific's rate limit. They allow 50 requests per minute, which means
one request every 1200ms. That extra 100ms was for clock drift between our boxes.
That was after I asked their support [041938c].
📜 041938c 2018-07-19 · Ezra Thorne · fix: throttle FirstPacific calls
gemma4:e2b-it-qat · on this machine · 814 tokens of memory in 1.6s · spoke 84 at 9.5 tok/s
It does three things:
-
git seance summon <file>:<line>finds who really wrote those lines (even through reverts). It gathers every change ever made to them, the comments that person left, and their habits, then opens a séance. -
git seance ask "<question>"searches the whole history, and whoever remembers best steps forward. -
git seance graveyardshows a tombstone for everyone who stopped committing, with how many of their lines are still running. Add--epitaphsand Gemma carves one for each of them.
How I read the theme. "Ghost in the Machine" is almost literal here: people who left years ago, whose decisions still execute every night at 2 a.m. But the idea I kept coming back to was Día de los Muertos: the dead aren't gone while someone still tells their stories. A git history is an ofrenda nobody visits. Commit messages are letters people left for whoever came next. git séance is a way to go and read them, in the voice of the person who wrote them.
Demo
Everything below is real output from Gemma 4 E2B on my laptop's CPU. The repository is fictional: hollow-creek-ledger, a tiny payments service with nine years of history and four developers, generated by a script in the repo so anyone can reproduce this demo exactly.
1. The graveyard.
⚰ THE GRAVEYARD OF HOLLOW-CREEK-LEDGER
4 spirits have committed here. 3 have departed (no commit in 365 days).
.-------------. .-------------. .-------------.
/ \ / \ / \
| | | | | |
| R . I . P | | R . I . P | | R . I . P |
| | | | | |
| Marisol Vega | | Priya Raman | | Ezra Thorne |
| 2017–2019 | | 2019–2021 | | 2018–2020 |
| | | | | |
| 76 lines | | 34 lines | | 21 lines |
| still linger | | still linger | | still linger |
| | | | | |
| "Settlement | | "Waiting for | | "Node 12 |
| waits for the | | the maintenance | | sleeps, keys |
| next hand." | | freeze to end." | | are locked." |
|_________________| |_________________| |_________________|
\|/,\|/,\|/,\|/,\|/ \|/,\|/,\|/,\|/,\|/ \|/,\|/,\|/,\|/,\|/
Among the living Dana Kim (7 days ago)
The epitaphs come from one Gemma call with a JSON schema, written from each person's commit messages. Marisol's last commit was literally "hand settlement over to ezra."
2. The revert. const MAX_ATTEMPTS = 7; is blamed on Dana, but only because in 2024 Dana lowered it to 3, watched 212 settlements fail overnight, and reverted. Plain git blame would summon the wrong person. git séance sees through the revert to the person who wrote the number:
$ git seance src/settlement.js:6 -q "Why is MAX_ATTEMPTS 7? Dana wants to lower it again."
👻 The spirit of Marisol Vega answers.
7 commits · 2017–2019 · departed 7 years ago · a night owl: 86% after dark
wrote src/settlement.js:6 on 2018-03-11: "fix: keep one more retry than FirstPacific's breaker"
seen through a revert: Dana Kim restored these words in 4d194b5
👻 The cold always settles deep into the code. I remember the reasoning
behind the seven attempts. It was because FirstPacific's gateway trips its
circuit breaker after six consecutive failures during the 02:00 batch
window, and needed one clean attempt to half-open again. That was measured
over nine nights, and zero settlements were abandoned [16d6516]. I left it
at seven because that accounted for tripping the breaker plus one more
attempt to find it half-open [16d6516]. Dana Kim later lowered it to three
in commit [d18b0da], but that was undone [4d194b5].
📜 16d6516 2018-03-11 · Marisol Vega · fix: keep one more retry than FirstPacific's breaker
📜 d18b0da 2024-02-06 · Dana Kim · perf: lower MAX_ATTEMPTS to 3
📜 4d194b5 2024-02-07 · Dana Kim · Revert "perf: lower MAX_ATTEMPTS to 3"
3. The ghost that doesn't know. One commit in the history is just fix: hc-0013, with no body, no ticket and no explanation. This is the answer I'm proudest of:
$ git seance src/settlement.js:9 -q "Why do we skip merchant HC-0013?"
👻 The cold always settles deep into these empty files. I remember adding
that line [8bba3e4] to skip merchant HC-0013. That was a fix for HC-0013
[8bba3e4]. I don't recall the specific reason why it needed skipping
beyond that commit message.
📜 8bba3e4 2019-02-14 · Marisol Vega · fix: hc-0013
Code
git séance 🕯
Summon the ghost of whoever wrote that line, and ask them why.
Every codebase has a comment like // don't touch. ask ezra. and Ezra left years ago
git séance reads the history Ezra left behind (blame, diffs, commit messages, the comments
they wrote) and lets you ask them, through an open-weight model running on your own machine.
$ git seance src/settlement.js:6 -q "Why is MAX_ATTEMPTS 7? Dana wants to lower it again."
👻 The spirit of Marisol Vega answers.
7 commits · 2017–2019 · departed 7 years ago · a night owl: 86% after dark
wrote src/settlement.js:6 on 2018-03-11: "fix: keep one more retry than FirstPacific's breaker"
seen through a revert: Dana Kim restored these words in 4d194b5
👻 The cold always settles deep into the bones when you ask about old
decisions. I set it to 7 because FirstPacific's gateway trips its circuit
breaker after…How I Built It
The medium: Gemma 4 E2B on a laptop CPU. My machine has Intel integrated graphics, 7.7 GB of RAM and no GPU. The model is gemma4:e2b-it-qat, the quantization-aware-trained 4-bit build of Gemma 4's smallest edge model, served by Ollama. Before designing anything I measured it:
- Generation: ~9–10 tokens/s. That's fine. It streams, and a ghost should speak slowly.
- Prompt reading: ~30–40 tokens/s. My first naive test handed it ~4,000 tokens of commit history and took 3 minutes 34 seconds to answer. That number shaped everything else.
Evidence, not vibes. The model never browses the repo. git séance builds a dossier with plain git and hands Gemma that and nothing else:
-
git blame --line-porcelain -w -Mfinds the author. - If the blamed commit is a revert, it re-blames the same line as it stood before the reverted commit. That's how Dana's revert leads back to Marisol.
-
git log -L 6,6:src/settlement.jsgives every change ever made to those lines, with the diffs. - It adds the comments they left elsewhere in the file, their own commit subjects (so the ghost sounds like them), and when they worked ("86% of commits between 22:00 and 05:00").
- For
ask, it ranks commits by embedding similarity (nomic-embed-text), plusgit log -Spickaxe searches for numbers and identifiers in your question. Asking about "1300ms" goes straight to the commit that added1300.
All of it is squeezed into about 1,300 tokens, trimmed in three passes (shorter bodies, fewer diff lines, fewer commits). --show-evidence prints exactly what Gemma sees, so nothing is hidden.
The dead cannot lie (mostly). Small models confabulate. Three things keep the ghost honest:
-
Every citation is checked. Each
[hash]in the answer is looked up in git afterwards. Real evidence gets a 📜 with the commit's actual subject line. A hash that doesn't exist gets a red "phantom: no such commit exists. Trust the record, not the ghost." - Missing reasons are marked out loud. A commit with an empty body shows up in the evidence as "(no explanation was written in this commit message)". Before I added that line, Gemma's answer about HC-0013 was "The reason is documented in the commit [8bba3e4]." It wasn't. One line of deterministic preprocessing fixed what no amount of prompt wording did.
- Rules go last. My instructions started at the top of the prompt, and Gemma mostly ignored the citation rule. Moving them below the evidence brought citations back on the very next run. A smaller lesson: one rule used to be headed "The dead cannot lie." Gemma began its next answer with, word for word, "The dead cannot lie." I renamed the rule.
It isn't perfect. Run the same question twice and you get two slightly different ghosts. Once, Marisol told me Dana "reverted it to three," which is backwards. That's exactly why the 📜 lines print each commit's real subject (Revert "perf: lower MAX_ATTEMPTS to 3") under the answer. The prose is the ghost; the citations are the record.
Designed around 8 GB.
-
Warm while you type. When an interactive séance opens, git séance quietly sends the evidence to Ollama with
num_predict: 1, and Ollama keeps it in its KV cache. When you press Enter, only your question has to be read. In my measurement prompt time dropped from 29.6 s to 0.5 s, so the ghost starts talking about 3 seconds after you finish typing. -
One model in the room. My Ollama runs with
OLLAMA_MAX_LOADED_MODELS=1, and swapping between the embedder and Gemma costs about 20 seconds each way. Soaskdoes all its embedding first and loads Gemma once, and follow-up questions use keyword retrieval instead of calling the embedder back. - Thinking off. Gemma 4 can reason before it answers. On a CPU that would eat the whole budget, so git séance turns it off when the model supports it.
Plumbing. Node 20+ with zero npm dependencies: git subprocesses, fetch to localhost:11434, and node:test. Name the binary git-seance and git picks it up as git seance for free. 23 tests run against the fictional repository with no model needed, and a GitHub Actions workflow runs them on Linux, macOS and Windows.
Why Does Open Innovation Matter?
Because a git history is one of the most private things a team owns.
-
It never leaves the machine. Commit messages name customers, incidents, partners and internal tickets. Plenty of teams aren't allowed to paste them into a hosted model at all. Out of the box, git séance never talks to anything but
127.0.0.1: blame, diffs and the embedding index stay local. The index lives in.git/seance/, so it's never committed. That means you can use it on the one repo you're not allowed to upload, which is usually the one with the most ghosts. - It works with the internet off. You go digging through old code during an outage, on a plane, or at 2 a.m. when the batch has failed. Those are exactly the moments you can't count on an API.
- It costs nothing to run. Embedding every commit in a repo, or asking forty questions while you debug, costs zero. I never once had to think about tokens as money, only as time.
- I could open the hood. Because I run the inference server, I could measure prompt throughput, pin the context size so the model never reloads, turn thinking off, and use the prefix cache to read the evidence while you type. The design came from those numbers. With a black-box API I'd have been guessing.
-
The medium is swappable. With
--model qwen3:4b-instructthe same séance speaks through a different model. I tried it: Qwen gave Ezra's answer correctly too, but at 3.8 tokens/s, against Gemma 4 E2B's ~10 on the same CPU. That gap is why Gemma is the default, and why a séance on a laptop feels like a conversation rather than a wait. When the next Gemma ships, it's oneollama pulland nothing else changes. - It won't disappear. A tool for remembering people shouldn't depend on a service that might shut down next year. The weights are a file on my disk. This séance will still work in ten years with no account.
Where would a closed model have been better? Honestly, a big hosted model would write better prose and need fewer guardrails. But the guardrails this small local model forced on me, evidence-only prompts, verified citations and explicit "no explanation" markers, are what make the answers worth trusting. I'd keep every one of them with a bigger model too.
My Agent Session
I built git séance with an AI coding agent (Claude Code), in one long session: measuring Gemma's real throughput on this laptop, writing the git plumbing and tests, and then many rounds of running real séances and tuning the evidence and prompt against what Gemma actually said. The HC-0013 fix and the "rules go last" lesson both came straight out of those runs.
The Spirit of It
Two moments landed for me while building this.
The first was the graveyard. 76 lines still linger. Marisol is fictional, but the shape isn't: in every real repository, people who left years ago are still running in production every night, and most of us couldn't name them.
The second was the ghost that admitted it didn't know. Asked about HC-0013, Marisol said she didn't recall the reason, because she never wrote it down. That's the most human answer git séance gives, and the most useful one, because it's a lesson for the living:
Write the commit message. Someday someone will light a candle, type your name, and ask you why.
Prize Categories
- Best Use of Gemma. Gemma 4 E2B (QAT, 4-bit), running locally through Ollama on a CPU-only laptop, is the medium every spirit speaks through. It also carves the graveyard epitaphs with structured JSON output, with thinking turned off so it fits a laptop's budget.
Top comments (0)