Skip to content
How much to read

Wiki Pipeline

A knowledge base that rewrites itself from my own repos every night, checked by a second model that has no tools and cannot go looking for agreement, plus a desktop app I use to drive the whole thing.

Read what it wrotewiki

The publishable pages only. The vault is larger; the rest is graded private, and the export refuses to write it. Not indexed by search engines, on purpose.

Role
Product, specification and review
Year
2026
Stack
Python, Claude, Obsidian, Electron, Windows Task Scheduler, git

At this depth1 min2 min10 min of 10

200 pagesrewritten from my repos nightly, checked by a second model

In short

Problem
A hand-written knowledge base behind this site that went stale the day the projects moved on, and that I would not keep up by hand.
User
One user, and that user is me. It is the caveat on every number here.
Decision
Let it publish without me. A process that needs me every day is one I abandon, and an abandoned process still looks like it is working.
Refused
No remote on the repository and no hosting for the vault, and no attempt to have it write anything a reader sees. I write those.
Number
200 pages written, and every one of them checked by a model that did not write it. One user, running hourly since 14 September 2026. Separately, 0 of 204 harvested items got past the redaction scan.

What this one proves

  • An agent pipeline built as two roles that do not report to each other: a writer that drafts, and a checker with no tools that is given the sources inline so it cannot go looking for confirmation.
  • Hidden thinking treated as a product constraint with a measured cost, after one uncapped checker spent about 21,000 tokens to make 3 edits and a zero-capped one approved a page in 4 seconds.
  • A desktop app that is read only except for one cell, because applying and being rejected are things a person does, not things an agent can observe.

That is the brief. Set the dial to 3 min for the argument, section by section, or Deep for all 10 minutes of it.

Where this is: running hourly since 14 September 2026, unattended. 200 pages so far, of which the publishable ones are readable at /wiki. It has one user and that user is me, which is the caveat on every number below.

The thing I was actually trying to fix

I had a knowledge base for this portfolio. I wrote it by hand at the end of August, consolidating four documents that had been quietly contradicting each other for months. It was good. It was also out of date within two weeks: the second version of EaseMySemester and the whole Clarity rebuild happened after it was written, and neither of them is in it.

That is the actual failure mode of a personal knowledge base. Not that it is badly organised. That maintaining it is a job, and it is the first job you drop when you are busy building the things it is supposed to describe.

So I stopped maintaining it. The current one reads my repositories and my project folders every day and writes itself. There is no step where I approve anything, because I know exactly how long I would keep doing that for.

Two roles that do not report to each other

The design rule is the same one I learned expensively on Clarity: the party producing evidence of correctness cannot be the party that grades it.

A writer model turns a batch of harvested source text into pages, against a style guide it is given inline. A second, different model then checks those pages. The checker has no tools. It cannot open a file, cannot search the repository, and cannot see the writer's reasoning. It gets the page and the source text that page was supposedly written from, in the same prompt, and it returns corrections.

Taking the tools away was deliberate and it is the decision I would defend hardest. A checker with a file system is a checker that can go and find something that agrees with the page. What I want is much narrower and much more useful: does this specific sentence appear in this specific source, or not.

Then a linter with no model in it at all runs last. It checks that the frontmatter is complete, that every source a page cites actually exists, that the page is between 40 and 900 words, that no placeholder text survived, that no em dash got in, and that nothing on a private list appears anywhere. A deterministic check is cheaper and more trustworthy than a model for anything that can be expressed as a rule, and roughly half of what people ask a verifier model to do can be.

Thinking is billed as output, which makes it a product decision

The first version of this cost far more than it should have and I did not notice for a day.

The checker was making small, sensible edits. Three of them on one page. It spent about 21,000 output tokens getting there, because hidden reasoning is billed as output whether you see it or not, and I had not capped it. On a plan with a finite allowance shared with everything else I do in a day, that is not an abstract inefficiency. It is the run stopping halfway through.

So I capped it. The cap is per step, because the steps do not need the same amount of thought: 8,000 tokens to write a page, 4,000 to verify one, and zero to repair a lint failure, which is mechanical.

Then I set the checker's cap to zero as an experiment. It approved a page in four seconds, having skipped most of a 22 KB source file. That is the other edge of the same decision, and it is the one worth writing down: cheap and broken is also expensive. The number I care about is not tokens per run, it is tokens per run at an output quality I would still put my name on, and the only way to find it was to run both ends and look at what came out.

A batch of about 40 KB of source now costs roughly 40 to 50 cents.

The run that set my money on fire

The first pipeline was all or nothing. It worked through a batch, and committed at the end.

On 14 September it hit the account's usage limit on the very last check call of a batch, threw an exception, and discarded about an hour of finished work, including pages that had already been written and verified and were sitting in memory waiting for a commit that never came. I paid for that work twice.

The rewrite treats a usage limit as an expected state rather than an error. Pages are committed five at a time as they pass, so finished work is on disk and in git before anything else is attempted. When a call comes back with a limit, whatever has not been committed is parked in a state directory, the time the limit resets is written down, and the run exits cleanly. The next run puts the parked pages back and continues from the step it was interrupted at. Nothing is ever redone.

The part I like best is small. The pipeline runs hourly, so a run stopped at 02:10 by a limit that resets at 02:40 would sit idle for fifty minutes waiting for the next slot. Instead the scheduler script reads the reset time and registers a one-off Windows task for a minute after it, copying its own settings across. The work resumes as soon as it is allowed to, not when the clock next happens to be convenient.

Redaction is a gate, not a promise

One of the sources is the workspace for the Clarity rebuild, and that workspace contains a real dental practice's real data. Another is my job search notes. Neither of those can end up in anything I publish.

The rules that came out of that:

  • Every source declares an explicit list of what to include. Never a list of what to exclude. An exclude list is a bet that you thought of everything, and you did not.
  • Patient names are read out of the clinic's own export at run time, held in memory, matched against, and never written anywhere. There is no list of names in this repository to leak.
  • After redaction, a second and independent scan looks at the output again and quarantines anything that still looks sensitive. First run: 204 items, none quarantined.
  • The pipeline's own configuration is one of its sources, which is how this case study exists. It prunes its private directory, its state directory and its own output, so it can never read a page it wrote last night and treat it as fresh evidence about the world.
  • Job search notes feed the job search pages only. They are never allowed to become a fact about a project or about my career.

Every page also carries a tier in its frontmatter: A is publishable and its caveat travels with it, B is for interviews, C never leaves the vault. The number on the front of this site comes out of a Tier A page, and if it were not Tier A it would not be on the front of this site.

That grading is now the thing the export enforces rather than the thing I remember. A page is graded as a whole, but a numbers page is a table of separately graded claims, and 15 rows sitting inside Tier A pages were graded below it. Publishing those pages because their frontmatter said A would have published those rows too. The export strips them, and then strips the tier column itself, because it is a note to me about what I may say and means nothing to a reader who is only ever shown one tier.

The 58 pages that passed the gate, and the 139 links between them. Drawn from the file the site renders rather than from the vault, so a link pointing at a page that did not pass is not drawn at all, and the other 80 pages are not in the picture. The six largest nodes are the indexes everything else hangs off.
The 58 pages that passed the gate, and the 139 links between them. Drawn from the file the site renders rather than from the vault, so a link pointing at a page that did not pass is not drawn at all, and the other 80 pages are not in the picture. The six largest nodes are the indexes everything else hangs off.

The numbers on the front page are pinned the same way. Each one names the wiki row it came from, and the site refuses to build if the figure it displays is not in the figure that row states. The reason that check exists is the reason this whole project exists: the previous knowledge base went stale in two weeks and nothing anywhere said so. Now a correction in the wiki breaks the export, which is loud, instead of leaving the site quietly wrong, which is not.

The app, and the one cell it is allowed to write

The pipeline has no interface. That is fine for the writing, and useless for the part where I actually use it, so there is a small Windows desktop app that reads the whole thing: every application in flight, every tailored resume, every session, every pipeline run with its model calls and what they cost, and the vault itself with its tier badges and working links.

It is read only, with exactly one exception, and the exception is the decision worth explaining. It can set an application's status. Nothing else.

Applying to a role, getting a screening call, being rejected: those are things that happen to a person in the world. No agent can observe them, so no agent can record them, so there has to be one place a human writes into the system. Making that the only write, and only from that application's own page, means the surface where I can damage my own data is one button on one screen. The save rewrites two cells of one row and leaves the rest of the table byte for byte identical, so the change shows up in git as a single line I can read and revert. A bulk edit from a table view would have been easier to build and much worse to trust.

It ships as one portable executable. No installer, no Python, no Node on the machine that runs it. No network calls at all, no telemetry, no auto updater. Everything it reads is on the disk in front of it.

What I chose not to build

No review gate, and this was the whole point. No approval step and no pull request to merge. The safety comes from the independent checker, the deterministic linter, and git as an undo button. A process that needs me every day is a process I will abandon, and an abandoned process is worse than no process because it still looks like it is working.

No web hosting for the vault. It is a local git repository with no remote and I am not going to add one.

No attempt to have it write the posts. It produces the facts, with their caveats and their tier, and I write anything anyone reads.

Where this can still fool me

The checker can prove a page faithfully reflects its source. It cannot prove the source is still true.

A fact harvested in September, written up accurately, and checked correctly stays on the page looking exactly as trustworthy in December, when the thing it described has changed and no source file happened to be touched. Faithfulness and currency are two different properties and I am currently only measuring one.

The partial answer is a weekly audit that rereads one project's pages in full, rotating through them, which catches slow drift but only if the drift is visible in the sources. The real answer is probably an expiry date in the frontmatter, so a number that nobody has re-derived in ninety days gets flagged rather than quietly aging into a lie. I have not built it yet, and until I do, that is the honest limit of this thing.

That is every section's opening. Deep has the working under each one, 10 minutes in all.

What is still open

Whether a checker that only ever sees a page and its sources can catch the failure I actually fear, which is a fact that was true the day it was written and quietly stopped being true. Matching a page to its source proves it was copied faithfully, not that the source is still the world.

Next

Semantic Search

Building the measurement loop before optimising anything, measure twice, cut once.

Get in touch

contact@adwitiyarahman.in

Happy to walk through any of this, including the parts that did not work. Or see the other projects.