<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pau López Núñez</title>
    <description>The latest articles on DEV Community by Pau López Núñez (@paulopnun).</description>
    <link>https://dev.to/paulopnun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4200857%2F1762dd2f-7f25-4da3-b5a6-f05108ef8a89.png</url>
      <title>DEV Community: Pau López Núñez</title>
      <link>https://dev.to/paulopnun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulopnun"/>
    <language>en</language>
    <item>
      <title>git-afterlife: I Held a Séance for the Original flask.py</title>
      <dc:creator>Pau López Núñez</dc:creator>
      <pubDate>Tue, 27 Oct 2026 18:06:17 +0000</pubDate>
      <link>https://dev.to/paulopnun/git-afterlife-i-held-a-seance-for-the-original-flaskpy-36o5</link>
      <guid>https://dev.to/paulopnun/git-afterlife-i-held-a-seance-for-the-original-flaskpy-36o5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Every repository has a graveyard.&lt;/p&gt;

&lt;p&gt;Somewhere in your &lt;code&gt;git log&lt;/code&gt; 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 &lt;code&gt;cleanup&lt;/code&gt;. Nobody lit a candle for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;git-afterlife&lt;/strong&gt; 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 &lt;em&gt;ofrenda&lt;/em&gt;, 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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;its &lt;strong&gt;birth date&lt;/strong&gt;, taken from the commit that created it,&lt;/li&gt;
&lt;li&gt;its &lt;strong&gt;death date&lt;/strong&gt; and its &lt;strong&gt;real cause of death&lt;/strong&gt;: the actual commit message that deleted it, never invented,&lt;/li&gt;
&lt;li&gt;the people who touched it, its &lt;strong&gt;mourners&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;its last known source code, as its portrait,&lt;/li&gt;
&lt;li&gt;and an &lt;strong&gt;epitaph and last words&lt;/strong&gt;, written by an open-weight model (Gemma 3) running locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then there is the part that gave me chills the first time it worked: &lt;strong&gt;&lt;code&gt;seance&lt;/code&gt;&lt;/strong&gt;. You pick one dead file and talk to its ghost. It answers in first person, from its real code and history.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn4es0ku338jg7pvwsmj9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn4es0ku338jg7pvwsmj9.gif" alt="A séance in the terminal with the original flask.py, recorded by GitHub Actions" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This is what happened when I asked it why it died:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;you ›&lt;/strong&gt; Why did they delete you?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;spirit ›&lt;/strong&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of the epitaphs Gemma wrote for the altar:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The deceased&lt;/th&gt;
&lt;th&gt;Epitaph&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flask.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"A microframework, purged with a dismissive 'out with the old.'"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tests/flask_tests.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Served Flask tests faithfully, then vanished with a split."&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setup.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"A simple setup, swiftly replaced by a modern plea."&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs/deploying/fastcgi.rst&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"It guided deployments, then vanished with a rewrite."&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;flask.py&lt;/code&gt;, then publishes everything to GitHub Pages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The altar:&lt;/strong&gt; &lt;a href="https://paulopnun.github.io/git-afterlife/" rel="noopener noreferrer"&gt;https://paulopnun.github.io/git-afterlife/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full séance transcript:&lt;/strong&gt; &lt;a href="https://paulopnun.github.io/git-afterlife/seance.txt" rel="noopener noreferrer"&gt;https://paulopnun.github.io/git-afterlife/seance.txt&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe9vztte24m0x3xf8qjuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe9vztte24m0x3xf8qjuj.png" alt="The altar raised for Flask: tiers of deleted files, each with its dates, epitaph, last words, cause of death and a recorded séance" width="800" height="938"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;séance&lt;/strong&gt;, and their conversation is shown on their card. Open any card's &lt;em&gt;portrait&lt;/em&gt; to read the file's last source code.&lt;/p&gt;

&lt;p&gt;Try it on your own repository in under a minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma3:4b      &lt;span class="c"&gt;# or gemma3:1b on an older laptop&lt;/span&gt;
git clone https://github.com/PauLopNun/git-afterlife.git
python3 git-afterlife/afterlife.py summon /path/to/your/repo     &lt;span class="c"&gt;# writes altar.html&lt;/span&gt;
python3 git-afterlife/afterlife.py seance some/deleted/file.py /path/to/your/repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or run it on any public repository without installing anything: fork the repo and use &lt;strong&gt;Actions → Raise the altar → Run workflow&lt;/strong&gt;, choosing the repository and the model.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/PauLopNun" rel="noopener noreferrer"&gt;
        PauLopNun
      &lt;/a&gt; / &lt;a href="https://github.com/PauLopNun/git-afterlife" rel="noopener noreferrer"&gt;
        git-afterlife
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Every repository has a graveyard. A Dia de los Muertos altar and seances for your deleted files, written by Gemma running locally.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/cc8a49853ce4f21e8113fb893fa0a8db48b895d5d63fe8dd2caac2125b15d9ac/68747470733a2f2f63617073756c652d72656e6465722e76657263656c2e6170702f6170693f747970653d776176696e67266865696768743d32323026636f6c6f723d303a3134306231662c35303a3762346262382c3130303a66663966316326746578743d6769742d61667465726c69666526666f6e74436f6c6f723d66366561643726666f6e7453697a653d363426666f6e74416c69676e593d333826646573633d41253230442543332541446125323064652532306c6f732532304d756572746f73253230616c746172253230666f72253230746865253230636f6465253230796f75722532307265706f7369746f72792532306275726965642664657363416c69676e593d3630266465736353697a653d313826616e696d6174696f6e3d66616465496e"&gt;&lt;img src="https://camo.githubusercontent.com/cc8a49853ce4f21e8113fb893fa0a8db48b895d5d63fe8dd2caac2125b15d9ac/68747470733a2f2f63617073756c652d72656e6465722e76657263656c2e6170702f6170693f747970653d776176696e67266865696768743d32323026636f6c6f723d303a3134306231662c35303a3762346262382c3130303a66663966316326746578743d6769742d61667465726c69666526666f6e74436f6c6f723d66366561643726666f6e7453697a653d363426666f6e74416c69676e593d333826646573633d41253230442543332541446125323064652532306c6f732532304d756572746f73253230616c746172253230666f72253230746865253230636f6465253230796f75722532307265706f7369746f72792532306275726965642664657363416c69676e593d3630266465736353697a653d313826616e696d6174696f6e3d66616465496e" alt="git-afterlife" width="100%"&gt;&lt;/a&gt;
&lt;p&gt;&lt;a href="https://paulopnun.github.io/git-afterlife/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2ff348681e629e161e2fb1212af30bde06a38670b7fc20ab8664f3afff82f9f7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c69766525323064656d6f2d746865253230616c7461722d6666396631633f7374796c653d666f722d7468652d6261646765266c6f676f3d6769746875627061676573266c6f676f436f6c6f723d7768697465" alt="Live altar"&gt;&lt;/a&gt;
&lt;a href="https://paulopnun.github.io/git-afterlife/seance.txt" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/69cc8be8081e3d7d5ab570e46328a34c0287d538f4f76685d14d478f9df70e9e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7472616e7363726970742d74686525323073254333254139616e63652d6538346138613f7374796c653d666f722d7468652d6261646765" alt="Séance"&gt;&lt;/a&gt;
&lt;a href="https://github.com/PauLopNun/git-afterlife/actions/workflows/afterlife.yml" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0c85708fbdf06395eccc2626e6e332379dca095e076996f3dc7b263d9977560b/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f5061754c6f704e756e2f6769742d61667465726c6966652f61667465726c6966652e796d6c3f6272616e63683d6d61696e267374796c653d666f722d7468652d6261646765266c6162656c3d616c746172266c6f676f3d676974687562616374696f6e73266c6f676f436f6c6f723d7768697465" alt="Raise the altar"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e8b9da4222dfcfc391eed014a05497eba6a14a2a3b0f618eafb604cc873369b2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e382532422d3337373641423f7374796c653d666c61742d737175617265266c6f676f3d707974686f6e266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/e8b9da4222dfcfc391eed014a05497eba6a14a2a3b0f618eafb604cc873369b2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e382532422d3337373641423f7374796c653d666c61742d737175617265266c6f676f3d707974686f6e266c6f676f436f6c6f723d7768697465" alt="Python"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/cb02010ed218a5428616e27b13ac1d6a87239f9632f8adca3867a7244a576502/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e732532306f6e2d4f6c6c616d612d3030303030303f7374796c653d666c61742d737175617265266c6f676f3d6f6c6c616d61266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/cb02010ed218a5428616e27b13ac1d6a87239f9632f8adca3867a7244a576502/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e732532306f6e2d4f6c6c616d612d3030303030303f7374796c653d666c61742d737175617265266c6f676f3d6f6c6c616d61266c6f676f436f6c6f723d7768697465" alt="Ollama"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/cefa1c5e12ddc548ccfdb4190ec31339698a422abe1a0103c6e850b65be56ee0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d6f64656c2d47656d6d61253230332d3432383546343f7374796c653d666c61742d737175617265266c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/cefa1c5e12ddc548ccfdb4190ec31339698a422abe1a0103c6e850b65be56ee0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d6f64656c2d47656d6d61253230332d3432383546343f7374796c653d666c61742d737175617265266c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465" alt="Gemma"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8d26010c9efe2747bb208cb153272dc788688b69d02c13780033c75edf20b2d5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646570656e64656e636965732d302d3265633462363f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/8d26010c9efe2747bb208cb153272dc788688b69d02c13780033c75edf20b2d5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646570656e64656e636965732d302d3265633462363f7374796c653d666c61742d737175617265" alt="Dependencies"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/be561db809f96f7333da245e6a227dc568e50527f2b1712a1a05b467c88fe095/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f776f726b732d6f66666c696e652d3762346262383f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/be561db809f96f7333da245e6a227dc568e50527f2b1712a1a05b467c88fe095/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f776f726b732d6f66666c696e652d3762346262383f7374796c653d666c61742d737175617265" alt="Offline"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/03af1c005eb6c7aed8a51351409a8260cf378962d3fccd5f9e2a7dd36b39c1d2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d6666643233663f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/03af1c005eb6c7aed8a51351409a8260cf378962d3fccd5f9e2a7dd36b39c1d2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d6666643233663f7374796c653d666c61742d737175617265" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://dev.to/challenges" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/f0ed219df49bf4915dd1e130886fb8098bdce30b2f5aab0c5aca5e32fa8bc63a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f62657266657374253230323032362d47686f7374253230696e2532307468652532304d616368696e652d6666366233353f7374796c653d666c61742d737175617265266c6f676f3d646576646f74746f266c6f676f436f6c6f723d7768697465" alt="Hacktoberfest 2026"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/bdc8bb740df8c726fac9f434a278d2ffef4cd0e21198b5122308f6739eea3c34/68747470733a2f2f736b696c6c69636f6e732e6465762f69636f6e733f693d707974686f6e2c6769742c6769746875622c676974687562616374696f6e732c68746d6c2c6373732c6c696e7578267468656d653d6461726b"&gt;&lt;img src="https://camo.githubusercontent.com/bdc8bb740df8c726fac9f434a278d2ffef4cd0e21198b5122308f6739eea3c34/68747470733a2f2f736b696c6c69636f6e732e6465762f69636f6e733f693d707974686f6e2c6769742c6769746875622c676974687562616374696f6e732c68746d6c2c6373732c6c696e7578267468656d653d6461726b" alt="Python, Git, GitHub, GitHub Actions, HTML, CSS, Linux"&gt;&lt;/a&gt;

&lt;p&gt;&lt;a href="https://paulopnun.github.io/git-afterlife/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/06065d3d9b572574151e1544122c0931b6e3be555ae732e197b1035e87cb1c9b/68747470733a2f2f726561646d652d747970696e672d7376672e64656d6f6c61622e636f6d3f666f6e743d466972612b436f64652673697a653d3230266475726174696f6e3d333530302670617573653d3132303026636f6c6f723d4646394631432663656e7465723d74727565267643656e7465723d747275652677696474683d373630266c696e65733d45766572792b7265706f7369746f72792b6861732b612b6772617665796172642e3b666c61736b2e70792b28323031302d30342d30362b2d2b323031302d30372d3032292533412b2532326f75742b776974682b7468652b6f6c642532323b4c696768742b612b63616e646c652b666f722b7468652b636f64652b796f752b64656c657465642e3b596f75722b636f64652b6e657665722b6c65617665732b796f75722b6d616368696e652e" alt="Every repository has a graveyard."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/749038d5b23d4d42b50d78f6c552a4336f3f384457ea0ad7f89fadd4f6a9c648/68747470733a2f2f7061756c6f706e756e2e6769746875622e696f2f6769742d61667465726c6966652f636f7665722e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/749038d5b23d4d42b50d78f6c552a4336f3f384457ea0ad7f89fadd4f6a9c648/68747470733a2f2f7061756c6f706e756e2e6769746875622e696f2f6769742d61667465726c6966652f636f7665722e706e67" alt="git-afterlife: a Día de los Muertos altar for the code your repository buried" width="100%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;git-afterlife&lt;/strong&gt; digs them out of your git history and builds them an altar, an &lt;em&gt;ofrenda&lt;/em&gt;
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 &lt;strong&gt;open-weight model running on your own machine&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can also hold a &lt;strong&gt;séance&lt;/strong&gt; and talk to the ghost of a deleted file. It
remembers its real code, who created it, and why it was removed.&lt;/p&gt;
&lt;div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;summon&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;seance&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Builds a self-contained HTML altar with papel picado, flickering candles, falling marigold petals and recorded séances&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/PauLopNun/git-afterlife" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The whole tool is one Python file that uses only the standard library, so there is nothing to &lt;code&gt;pip install&lt;/code&gt;. MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The open pieces:&lt;/strong&gt; &lt;a href="https://ai.google.dev/gemma" rel="noopener noreferrer"&gt;Gemma 3&lt;/a&gt;, Google's open-weight model, served by &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. git-afterlife talks to Ollama's HTTP API on &lt;code&gt;localhost&lt;/code&gt; with plain &lt;code&gt;urllib&lt;/code&gt;. There is no SDK and no cloud endpoint anywhere in the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Git archaeology.&lt;/strong&gt; &lt;code&gt;git log --diff-filter=D&lt;/code&gt; lists every deletion in the history. Files that were later resurrected at &lt;code&gt;HEAD&lt;/code&gt; are skipped, and so are binaries. To decide who gets a place on the altar, one more &lt;code&gt;git log --name-only&lt;/code&gt; 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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git log -- &amp;lt;path&amp;gt;&lt;/code&gt; up to the death commit gives its birth, its lifespan in commits and its mourners,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git show &amp;lt;death&amp;gt;^:&amp;lt;path&amp;gt;&lt;/code&gt; recovers its final source code,&lt;/li&gt;
&lt;li&gt;a few regexes pull out what the file would &lt;em&gt;remember&lt;/em&gt; about itself: its function and class names, or its section headings if it was documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Grounding a small model.&lt;/strong&gt; My first run with Gemma 3 1B worked end to end, and it was bad. Every epitaph was some variation of &lt;em&gt;"lost in the digital dust, a silent plea"&lt;/em&gt;, asking for another language with &lt;code&gt;--lang&lt;/code&gt; was mostly ignored, and in the séance &lt;code&gt;flask.py&lt;/code&gt; claimed it had been deleted because of &lt;em&gt;"a subtle bug"&lt;/em&gt;. That was invented: the commit says &lt;code&gt;out with the old&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Three changes fixed most of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Feed it facts it can quote.&lt;/strong&gt; The prompt now lists the file's real function names, and the model is required to mention at least one, plus the real commit message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ban the clichés by name.&lt;/strong&gt; Small models fall back on the same few spooky words. Listing them as forbidden ("digital dust, echoes, void, silent plea…") and giving one short example in the tone of a &lt;em&gt;calavera literaria&lt;/em&gt; changed the output more than any amount of "be creative". It also taught me how literally a small model reads its examples: in one run, Flask's old test suite wistfully told me &lt;em&gt;"Perhaps I should have embraced dateutil sooner."&lt;/em&gt; &lt;code&gt;dateutil&lt;/code&gt; only appeared in my example. Every ofrenda has one guest who talks too much.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the truth non-negotiable.&lt;/strong&gt; In the séance, the system prompt states who deleted the file and with which message, and tells the ghost to quote it and never invent another reason. The cause of death printed on the altar doesn't come from the model at all: it is the commit message itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Recorded séances.&lt;/strong&gt; &lt;code&gt;summon --seances 3&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Don't trust a ghost that won't stop talking.&lt;/strong&gt; Epitaphs are requested in Ollama's JSON mode (&lt;code&gt;"format": "json"&lt;/code&gt;), 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 (&lt;code&gt;num_predict&lt;/code&gt;), 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The altar.&lt;/strong&gt; The HTML is generated with plain string templates and CSS animations: no JavaScript framework and no external requests, so the page works offline too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Tests and the demo pipeline.&lt;/strong&gt; 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 &lt;code&gt;ubuntu-latest&lt;/code&gt; runner, pulls Gemma 3 4B, runs everything on CPU, takes the screenshots in this post with headless Chrome, records the séance GIF with &lt;a href="https://github.com/charmbracelet/vhs" rel="noopener noreferrer"&gt;VHS&lt;/a&gt; and deploys it all to GitHub Pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Because the thing git-afterlife reads is &lt;strong&gt;your source code&lt;/strong&gt;, 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 &lt;code&gt;localhost&lt;/code&gt;, it doesn't. You can hold a séance for your company's legacy billing module without sending a single line of it anywhere.&lt;/p&gt;

&lt;p&gt;It also means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works with the lights, and the internet, off.&lt;/strong&gt; Once the model is pulled, nothing needs a network connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing.&lt;/strong&gt; No API key and no per-token bill. The entire public demo, with the altar, four recorded séances and the GIF in this post, runs for free on a stock GitHub runner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can swap the medium.&lt;/strong&gt; &lt;code&gt;--model&lt;/code&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It won't disappear.&lt;/strong&gt; An ofrenda is about memory that lasts. A tool that remembers your project's past shouldn't depend on a service that might be shut down, change its pricing or retire the model next year. The weights are on your disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Spirit of It
&lt;/h2&gt;

&lt;p&gt;The moment it landed was the last question of the séance. I asked the original &lt;code&gt;flask.py&lt;/code&gt;, 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 &lt;code&gt;Request&lt;/code&gt;, &lt;code&gt;Response&lt;/code&gt; and &lt;code&gt;Session&lt;/code&gt;. Then it turned its own cause of death back on us:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;spirit ›&lt;/strong&gt; 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."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Run it on your oldest repository. You will probably find a file you had forgotten you wrote. Light it a candle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 writes every epitaph and speaks for every ghost, running locally through Ollama (Gemma 3 4B by default, 1B for older machines). The whole project was shaped around getting a small open model to tell the truth: grounding it in real function names and commit messages, banning its clichés, and validating its JSON.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This post and the project were made with the help of AI coding tools. I reviewed, ran and tested everything that is described here.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>git</category>
    </item>
  </channel>
</rss>
