❯ ai · Running

Le wiki que mon IA devait tenir à jour : autopsie à J+4 The Wiki My AI Was Supposed to Keep Up to Date: a Four-Day Autopsy

Un LLM Wiki à la Karpathy, alimenté par un agent quotidien et surveillé par un lint hebdomadaire dans le cluster. Quatre jours après sa création, j'ai ouvert le capot et compté : une journée à 17 commits d'infra a laissé 2 commits dans le wiki, les captures n'étaient jamais commitées, et quatre pannes empilées fermaient la vanne. Ce qui a cassé, ce qu'a donné la réparation, et pourquoi le coût de maintenance n'a pas disparu : il a changé de nature. An LLM Wiki in Karpathy's style, fed by a daily agent and watched by a weekly lint in the cluster. Four days in, I opened the hood and counted: a 17-commit day left 2 commits in the wiki, captures were never committed, and four stacked failures kept the feed shut. Here is what broke, what the fix looked like, and why the maintenance cost did not disappear — it moved.

· 11 min de lecture 11 min read ·
aillmgitopsk3s-clustercidebugging
🇫🇷 Lire cet article en français

The bet: a wiki the AI maintains and the human validates

In April 2026, Andrej Karpathy published a short text, the “LLM Wiki”. Three layers: raw sources you never edit, a Markdown wiki written by the model, and a schema file that sets the rules. Three operations: ingest, query, lint. The thesis fits in one sentence: humans abandon their wikis because maintenance is tedious, and with an LLM the maintenance cost goes to zero.

I took him at his word, but for a different use than the readings he had in mind: the steering register of a freelancer. About twenty projects, client and personal, with their status, next action and the decisions taken. The starting point was ordinary: documentation scattered across Notion, Obsidian, a drive and conversations with the AI, plus a healthy distrust of hallucinations.

What got built in one evening, on September 28:

  • a Git repository on my self-hosted forge, opened in Obsidian, with a CLAUDE.md acting as a constitution;
  • an inbox/ fed by two agents, a daily one (the one that maintains this blog) and a weekly sync from Notion; they are only allowed to drop captures there;
  • /ingest, with a human in the loop, to merge those captures into the pages;
  • a dependency-free Python lint, run by a cluster CronJob every Monday, report on Telegram.

The truth rules are what I keep as the best part. Every fact carries a source and a date. A ”?” beats a guess. When information is missing, the answer is “Not in the wiki”, never an improvisation. And a five-step delivery scale: specified → coded → tested → pre-prod → verified in service, the last one requiring the output of the proving command to be pasted into the page. A test receipt, applied to documentation.

The return on investment came within twenty-four hours: an administrative follow-up forgotten for weeks, two reference documents that contradicted each other, a sent document nobody could tell which version had gone out, and nine actions sorted by priority. None of the previous tools had produced that.

The numbers at day 4: the wiki was not keeping up

On October 2, I did what you do with any system you just put into production: count.

MeasureValue
Pages30, of which 20 projects and 4 decisions
Versioned lines24,086, of which 1,689 of Markdown (7 %)
Open points ”? / to confirm”42
Wiki commits8 on Sept 28–29 · 0 on Sept 30 and Oct 1 · 2 on Oct 2
Infra repo commits on Oct 217
/ingest, /query, /lint typed as commands0

October 2 is a good full-scale test. That day the GitOps repository receives 17 commits: three sites and the blog move to a new deployment pattern, one application is removed and redeployed under another name, two tools move out to their own repositories. The wiki receives 2 commits. The new application has no page. The pages of the migrated sites say nothing about the migration. The same day, my assistant’s automatic memory modifies 6 files, including a complete sheet on the new application.

The 93 % of lines that are not Markdown? An Obsidian query plugin, installed the first evening, used on no page. And the most-cited page of the wiki was not the portfolio but the migration catalogue: the scaffolding was still holding up the building.

The daily agent had seen everything, though. On October 1, its log notes that an active project’s page is behind. On the 2nd, it finds two projects without a page and one page even more outdated. And it dropped nothing in the inbox. Walking back up the chain, I found not one failure but four, stacked, each hiding the next.

Link 1, quota. The agent runs on my subscription, not on an API key; before writing anything it reads the consumption published by a small collector. The PowerShell launcher read that series over a 48-hour window and found 8 %: green light. The skill re-read the same series over a 15-minute window, fell into a collection gap, and concluded “empty answer, caution”. Two windows for one measurement.

Link 2, caution. Unreadable quota, the run switched to “detection only”. Sensible for writing a blog article, the expensive step. Absurd for a ten-line capture that costs almost nothing. The rule did not tell them apart.

Link 3, the queue. “Project” items were processed like any other: three items per run, oldest first. The six pending project items sat at ranks 22 to 33 of 33, behind a backlog of articles and doc drifts. Even with the valve open, the wiki would have waited more than a week.

Link 4, the commit. The founding decision said: the agent drops files, “Obsidian Git handles the rest”. The plugin was configured properly, backup every 15 minutes. It produced zero commits since the wiki was created, because Obsidian was simply not open on that PC. A dropped capture would have stayed a local file, invisible to the forge and to the cluster lint.

The weekly lint, meanwhile, was impeccable. Non-root pod, read-only filesystem, no capabilities, no git: it fetches an archive of the repository through the forge API. It checked the wiki’s form: frontmatter, links, declared dates. It did not know an application existed without a page. The one that knew lived in another system.

The lint that cried wolf

In passing, three defects of the lint itself, because they are instructive.

The open-points counter detected ? and “to confirm” separately. But the wiki’s convention writes ”? to confirm”: every point was counted twice. The 42 open points were really 22. Better still: the page describing the knowledge system pasted the lint output as proof of commissioning, output that contains the string ”? / to confirm”. The lint was detecting itself. The proof ritual was polluting the signal.

The only red item, a public URL answering 530 because the server is not installed yet, was known and accepted. With no way to acknowledge it, every Monday report opened with “red: to fix”.

And the verification date was stored twice, in the portfolio and in each page: two discrepancies out of twenty after four days.

Batch 1: fix the feed before adding features

The whole of batch 1 fits in half a day and four moves, each covered by a test that failed before going green.

  1. One quota window, 48 hours, read the same way by the launcher and the skill.
  2. Unreadable quota → captures only, never “nothing” again. Article writing stays subject to quota.
  3. Project items go first, outside the three-item cap, ten at most per run.
  4. The launcher commits the captures. A PowerShell script, no LLM, that adds only the new inbox/YYYY-MM-DD-*.md files, pushes the main branch, rebases while preserving work in progress if the remote moved, and does nothing if the repository is not on main. Twenty-five checks against a real throwaway git repository: rebase, push refused then retried on the next run, wrong branch.

And for the lint: ”? to confirm” counted once, code and quotations ignored, a connu_jusqu_au: field that acknowledges a known URL failure until a date, and a new check: a history entry that says “verified” without verifie_le moving is flagged.

The founding decision was replaced by a new one, as the wiki demands of itself: you do not reopen a decision, you write another one with remplace:.

Batch 2: measure instead of declare

Batch 1 reopened the valve. Batch 2 tackles the real problem: a verifie_le is a declaration, and a declaration goes stale silently.

Coverage. Each page claims its repositories in the frontmatter: depots: [repo-name, other]. The lint lists the forge’s repositories through the API, with the read-only token already mounted in the pod. Any non-archived repository pushed within the last 30 days that no page claims comes out red: “project without a page”. Repositories without a project are declared on a dedicated page, with the reason. First reading: 22 active repositories, all claimed.

Freshness. Same field: if a repository received commits after verifie_le, the page comes out yellow, “N commits since verification”. First reading: 5 pages to re-verify, including a “parked” page whose repository had received more than 50 commits since its verification date. Exactly what the agent had seen the day before, but computed every Monday by the cluster, without a single model call.

Two robustness details that an independent review raised and the tests now cover: an API error on a single repository must not cancel the whole check (each failing repository comes out alone in yellow), and pagination beyond 50 repositories is verified by mutation, deleting the loop to watch the test fail.

The portfolio is generated. A script rebuilds the portfolio tables between two HTML markers, from statut, a new action: field and verifie_le. The drifting duplicate disappears, and so does the query plugin: 20,875 lines gone.

/ingest goes through a PR. An ingest/… branch, a pull request opened by a script (in UTF-8 JSON: the forge API rejects a curl body whose accents are mangled), a CI job on the NAS runner that runs the 47 tests, checks that the portfolio is up to date and runs the lint without network. I merge from my phone. “The AI maintains, the human validates” stops being an end-of-session ritual and becomes a mechanism.

Day 5: the chain runs, and catches what I missed

October 3, 12:30, first complete run after the repair.

StepOct 2Oct 3
Quota read8 %, then “empty”35 %, session 3 %
Processingskipped12 items in 8 min
Project items processed09 of 9, first
Captures dropped05
Commit on the forgenone”inbox: 5 capture(s)”, 2 seconds after the run

Four of the nine items were closed without a capture, with an exact line reference to the page that already covered the fact: deduplication works. The five remaining captures are sourced (commits, kubectl, curl, docker ps), each with an integration suggestion per page.

Two of them fix gaps in my own manual catch-up from the day before: a service deployed on October 1 and a new infrastructure map, both forgotten. A third replaces a ”? to confirm” with a docker ps output. That is the expected added value: the wiki no longer depends on what I remember to write.

The first /ingest as a PR followed within the hour: green CI, merge, empty inbox, 0 red in the lint.

What I take away

Karpathy is right, and it is subtler than it looks. Writing costs almost nothing anymore. What costs is triggering the writing. Since the wiki was created, none of the three commands had been typed. The rule “every session ends with /ingest” is a ritual, and a ritual always loses against a side effect. My assistant’s automatic memory was winning because it writes itself during the work, not after.

Knowledge accumulates, state expires. The LLM Wiki model targets knowledge that accumulates: readings, concepts. I store in it a state that expires every day. A project status looks more like a metric than a note: it has to be measured, and the most honest measurement is the git repository itself.

Test the chain, not the links. Each link worked “in principle”. No test crossed the whole chain, from detector to commit on the forge. The question I failed to ask on September 28: “if the agent finds something tomorrow at noon, where is the commit at 12:45?”

A permanent red is an invisible red. The lint produced the same red item every week for a known cause. By the second report, nobody reads it anymore. Acknowledging with an expiry date costs one line of frontmatter and saves the signal.

Tests caught what reviews missed. The BOM that PowerShell 5.1 slips into a JSON file, the leading space that Out-String().Trim() eats from git status --porcelain output, the # of a URL mistaken for a frontmatter comment: three real bugs, found by tests written before the code, against real throwaway git repositories and a fake API server.

One honest open question remains: a read-only agent is soon to answer questions from this wiki. Before plugging it in, I want a set of twenty questions, five of them with no answer in the wiki, and to measure how often the system correctly answers “Not in the wiki”. The anti-hallucination promise will become a number. Or not.

aillmgitopsk3s-clustercidebugging