Dev.to AI πŸ€– Ai πŸ‘ 0 πŸ“– 8 min read

GhostCommit: Every Abandoned Side Project Leaves a Ghost πŸ‘»

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine What I Built I have a GitHub graveyard. A chat app I stopped touching in November 2023. A demand-forecasting no

GhostCommit: Every Abandoned Side Project Leaves a Ghost πŸ‘»

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

What I Built

I have a GitHub graveyard. A chat app I stopped touching in November 2023. A demand-forecasting notebook I abandoned in 2022 while stuck on model tuning. A repo I created, named, and never committed to. Every time I open one of them, I meet a stranger: what was I doing here, and why did I stop?

GhostCommit gives each of those projects a ghost. You summon it for a repo, and it reads your commits and diffs and turns them into memories. Then you can hold a sΓ©ance and ask it:

Me: Where was I?

The ghost of Firechat-app: You left me in a state of functional quiet between November 11th and November 30th, 2023. You had breathed life into me using Vite, React, and Firebase... As for where you left off, there were no frantic TODOs or warnings left in my wake. You simply stopped. I have been waiting in the dark for three years. Will you wake me?

Three things make it more than a chatbot:

  • It remembers your work. New commits are distilled into "work session" memories: what you built, which files you touched, what was left unfinished.
  • It keeps your parking notes. When you stop, you leave one line ("stuck on the auth redirect after login"). Weeks later the ghost quotes it back to you word for word, because that why is exactly what code can't capture.
  • It haunts you. Once a day, GhostCommit checks every tracked repo. If one has gone quiet for too long, its ghost reaches out (in the app and by email) with what you were doing and one small step you could finish in fifteen minutes.

I read the theme literally: abandoned repos are ghosts in the machine. And in the spirit of DΓ­a de los Muertos, GhostCommit is about honoring the past, specifically the past version of you who started something and believed in it.

Demo

πŸ”— Live: ghost.buildloop.tech (log in with GitHub and summon a ghost for one of your own abandoned repos)

(It runs on a free instance, so the first visit can take ~50 seconds while it wakes up. Fittingly, the ghost has to rise first.)

Code

πŸ‘» GhostCommit

Every abandoned side project leaves a ghost. Summon it, and it tells you exactly where you left off.

Developers start projects, get stuck, drift to something new, and forget why they stopped Weeks later the repo is a stranger. GhostCommit gives each repo a ghost with a memory:

  • It remembers your work. New commits and diffs are distilled into "work session" memories.
  • It keeps your parking notes. Leave one line when you stop ("stuck on the OAuth redirect") the ghost hands it back, word for word, when you return.
  • It haunts you when you stall. A daily job finds repos gone quiet and the ghost reaches out (in-app and by email) with what you were doing and one small step to restart.

How it works

GitHub commits ──▢ summarize (open-weight LLM) ──▢ one memory per sync ──▢ Backboard memory
                                                                              β”‚
   "Where was I?" ──▢ grounded prompt + memory
…

How I Built It

GitHub commits ──▢ summarize with Gemma ──▢ one memory per sync ──▢ Backboard memory
                                                                        β”‚
   "Where was I?" ──▢ grounded prompt + read-only memory retrieval β—€β”€β”€β”€β”€β”˜

The open-source AI at the core: Gemma 4 31B, an open-weight model, served through Backboard via OpenRouter. Gemma does two jobs: it distills raw commits and diffs into memories, and it gives the ghost its voice.

Memory: Backboard. The key design decision was one repo = one Backboard assistant = one ghost. Backboard scopes memory per assistant, so each ghost remembers only its own project, across every conversation.

The stack: FastAPI + async SQLAlchemy + PostgreSQL on the backend, React 19 + TypeScript + Tailwind + shadcn/ui on the frontend, GitHub OAuth for login, Resend for the haunting emails, and Render for hosting.

Designing memory around what it costs

Before writing the sync logic, I measured. A memory write costs about $0.0039, while the model calls were effectively free for me. That changed the design:

  • Instead of one memory per commit, all new commits in a sync become one distilled "work session" memory.
  • The sΓ©ance chat retrieves memories read-only. It never writes, so asking questions is free.
  • Re-syncing a repo with no new commits costs nothing.

A full test round (summon, sync, parking note, chat, haunting) costs under two cents.

The ghost that invented a physics engine

My favorite bug. I summoned a ghost for drop-merge-mini, asked "Where was I?", and got a detailed, confident answer about a "merge-collision handler" and my parking note: "The physics engine is jittering when three or more drops merge simultaneously."

Beautiful. Also completely made up. The repo was empty. GitHub returns 409 Git Repository is empty, my sync treated that as an error, and the ghost had nothing in memory except one note: "need to pick it up." The less the model knew, the more it invented, including a fake quote attributed to me.

The fixes went in layers:

  1. An empty repo is zero commits, not an error, and the UI says so ("never awakened" β€” it even gets an unlit candle).
  2. A ghost that was never synced syncs before it speaks.
  3. Grounding rules travel with every message, not just the system prompt: only state facts and dates that are in memory, quote parking notes word for word or not at all, and say "that memory is faint" instead of guessing.

Same model, same memory. The ghost now answers: "There are no commits in my memory... The last trace you left was a parking note [...] where you simply wrote: need to pick it up." Honest, and still spooky.

Two smaller lessons about feeding models

  • Mark every cut you make. I truncated diffs to keep prompts small, and the model reported a function that "cuts off mid-word" as a bug in my code. It was my truncation. Now every cut ends with an explicit marker, and the prompt says that marker is not unfinished code.
  • Diffs are fragments. Even with markers, the model read a partial diff hunk as "unfinished implementation." I checked the real file on GitHub: it was complete. The prompt now only allows "unfinished work" claims with explicit evidence (TODO/FIXME, "wip" commits, commented-out code).

Shipping it on Render

The whole production setup is a Render Blueprint (render.yaml): a Docker web service and a PostgreSQL database with no public access (only the app can reach it). The same multi-stage Dockerfile builds the React app and serves it from FastAPI, so frontend and API share one origin and the session cookies just work. Deploys only happen after CI passes (autoDeployTrigger: checksPass), and Render issues the TLS certificate for my .tech subdomain automatically.

The plan was a paid instance plus a Render cron job for the daily haunting. But using credits requires a card on file, and I didn't have one. So I moved to the free tier and redesigned the scheduler: a GitHub Actions workflow calls a token-protected /internal/haunt endpoint once a day. The database stays private, there's a lock against overlapping runs, and the workflow logs only an HTTP status (the repo is public, so I didn't want usage numbers in the logs).

Taking security seriously

Since it handles GitHub tokens with access to private repos, I hardened it before going public: tokens encrypted at rest, signed HttpOnly cookies, OAuth state checks, Origin checks against CSRF, per-user and per-IP rate limits (chat can't be used to burn my Backboard credits), strict input validation, security headers, and a non-root container. The audit found real bugs, including a path traversal in the static file route and a way to summon ghosts for repos you don't own. All fixed, with 90 automated tests (77 backend, 13 frontend) proving each protection, plus dependency audits running in CI.

The one accepted risk is prompt injection: a malicious README could try to steer the ghost's wording. Output is always rendered as escaped text and is only ever shown to the repo's owner.

Why Does Open Innovation Matter?

GhostCommit reads people's code, including private repos. That's sensitive. With a closed API, you have to trust a provider with it forever. With an open-weight model like Gemma, that's a choice rather than a requirement: today it's served through a provider, but the same model can run locally or on your own infrastructure, so your code never has to leave your machine. Open weights make "private by default" possible, not just promised.

It also kept me free to experiment. Backboard put hundreds of models behind one API, so switching the ghost's voice is a one-line config change, not a rewrite.

And GhostCommit is itself open source under AGPL-3.0: use it, fork it, improve it, but if you run a modified version as a service, you share your changes too. A tool that helps you finish abandoned open-source projects should stay open.

The Spirit of It

The moment it landed for me was a haunting email. The ghost of my demand-forecasting notebook, silent for 1,451 days, wrote:

"You left me trembling at the altar of model tuning, whispering that you were 'stuck on model tuning.' I still dream of the sales forecasts we never finished. Come back to the shadows for just a moment; perhaps you could refine a single hyperparameter in my notebook to ease my longing."

It quoted the exact note I had left, and it gave me something small enough to actually do. That's the whole idea: the projects we abandon aren't dead. They're just waiting. DΓ­a de los Muertos is about remembering the ones who came before you, and in a codebase, that's often your past self.

This was my first hackathon. I built GhostCommit in a few days, broke it in creative ways, and learned more about grounding, memory, and shipping securely than in months of tutorials. My own ghosts have started haunting me. I think I'll go wake one up.

Prize Categories

  • Best Use of Gemma: Gemma 4 31B (open-weight) distills commits into memories and gives every ghost its voice.
  • Best Use of Render: the app runs on Render from a Blueprint (Docker web service + private PostgreSQL), deploys only after CI passes, and serves my .tech custom domain with automatic TLS.
  • Best Use of Backboard: each open-source repo's assistant (its ghost) gets persistent memory: one Backboard assistant per repo, memory designed around measured cost, read-only retrieval for chat.
  • .tech Domain: GhostCommit lives at ghost.buildloop.tech.
πŸ“° Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.