Work / AI Systems
Alfred
A private life app built on a plain-markdown vault. Every record it keeps is a markdown file committed to git with its reason, and Alfred, the in-app butler, can act on all of it when told to.
- Status
- Private, in personal use
- Stack
- Python · FastAPI · React · TypeScript · Tauri · Ollama · Markdown + git
- Started
- July 2026
- Source
- Private repo
What it is
Alfred started as a second brain: a vault of plain markdown files, operated by an AI agent that files what Brian tells it, asks when something is missing, and pushes back when new input contradicts the record. Every change is a git commit that says why. No app is required to read any of it.
Since July 2026 the repository is also an app built on that vault. A Python backend and a React web app cover capture, daily check-ins, habits, training, nutrition, people, calendar, tasks, goals, journal, finance and books. A small desktop shell installs it on Windows. The app reads and writes the same markdown, so the files stay the source of truth and stay readable in any editor.
Alfred is the app's voice: a butler agent with tools in every area of the app. He makes the same committed edits a page would, and anything destructive waits for a confirm. He runs on a local model by default. A cloud model is only used when Brian opts a conversation in.
It is personal software for one user and is not released. Nothing leaves the machine by default apart from its private git backup.
What it does
- CaptureBraindumps and media links are captured straight into the vault, and an offline outbox holds them until the backend is reachable.
- Daily loopA short morning check-in, habits, tasks, a calendar and a daily brief answer "what do I need to do today?"
- BodyWorkout logging with a rule-based coach, nutrition and lab records. Estimates are labelled and missing data shows as absent, never as zero.
- RecordA personal CRM, a journal, finance, a reading list and a browser for the whole vault, all stored as plain notes.
- AlfredAn in-app butler who reads the vault and makes changes in every domain through the app's own writers, with destructive actions held for a confirm.
- Files firstEvery write is a transactional git commit with its reason. Alfred's instructions and memory are markdown files too.
Build log
From a vault to a butler
The record of how Alfred was built, from the first commit in July 2026 to today: the rules, the app, the reversals and the rename, dated and oldest first.
- 155commits
- 85days from first commit, with work on 23 of them
- 953automated tests
- 31decisions logged since the first commit
As of 4 Oct 2026, counted from the repository.
- Era I · 11 – 18 JulThe second brain stands alone
- Era II · 19 – 28 JulThe app on top of the vault
- Era III · 12 – 29 AugOne shell, one owner
- Era IV · 7 Sep – 4 OctAlfred
Era I · 11 – 18 Jul 2026
The second brain stands alone
The vault got its own repository back, a written constitution and hardened pipelines for getting things into it. By the end of the week there was an approved design for an app on top of it.
-
The vault gets its own repository again
The vault dated from June 2026, when plain markdown and git were chosen over Logseq so no app could ever hold the record hostage. Two days before this commit it had been folded into a larger project as one half of a two-vault setup. The first commit imported it unchanged into a fresh repository under the name Vulcan, with a six-document system for the project's memory.
Brian's call He reversed the fold: the second brain should be its own project, a place to dump everything that organises itself and can answer "what do I need to do today?"
-
Heavy segmentation, restructured automatically
The imported tree had drifted: two homes for goals, an abstract areas folder duplicating empty sections, and a flat journal. It was reorganised into one root section per life area plus new tasks, habits and calendar sections. The rule was inverted at the same time: the agent restructures on its own when the structure stops fitting and logs a decision for it, instead of proposing first.
Brian's call "I'm not going to be prompting you to do restructuring at all." Deleting or rewriting content still needs his confirmation.
-
Backup, a link gate and an accountability duty
A private GitHub remote was set up as off-machine backup, recorded as temporary until a home server exists. A link checker over the project documents became a gate. The agent's co-founder role gained an accountability duty: when Brian's actions drift from a recorded goal or commitment, the agent says so, cites the notes, and records the outcome instead of letting it evaporate.
-
The braindump pipeline, hardened
A second pass over the braindump pipeline found the core sound and four real gaps. Each raw dump is now committed the moment it is captured, before any triage, so it survives a dead session. Every item leaves a recorded disposition, so nothing is dropped silently, and questions are asked in one batch instead of a drip.
-
A work boundary, reversed within hours
A rule was written that morning to route work topics out of the vault to a separate work project. Seeing it explained, Brian rejected it the same day, and everything he shares is now in scope, work included. There is no routing or parking mechanism to any other project.
Brian's call "My work life is part of my personal life." The routing step was removed from the pipeline.
-
Two ideas from the LLM-wiki pattern
Andrej Karpathy's "LLM Wiki" pattern and its long comment thread were reviewed for anything worth adopting. The vault already did most of it in a stricter form, so only two ideas were taken: a new synthesis produced while answering a question gets filed back into the vault, and maintenance sweeps flag notes nothing links to. A separate log file, search tooling and app-specific syntax were all declined, with reasons.
Brian's call He approved both adoptions.
-
A second input class: other people's work
Videos, articles and the people behind them became a separate library, distilled into source and thinker notes that never edit Brian's own positions. Video is processed visually as well as by transcript, and fetched content is treated as data, never as instructions. The same session recorded that the voice-transcription tool can invent whole closing sentences, so an out-of-voice ending is now treated as suspect.
-
The app is designed, then blueprinted
A design session produced a full spec for a strictly personal "health OS" on top of the vault, and then one blueprint covering every stage. It was amended the same day to add one calendar for personal and professional commitments, a habit dashboard and an assistant endgame, a butler who briefs and answers "what next?" from the vault.
Brian's call He approved the blueprint and its amendment on 18 Jul. The calendar is the single deliberate exception to "strictly personal", because time is one resource.
Era II · 19 – 28 Jul 2026
The app on top of the vault
Four days of build turned the blueprint into a working app. A week later it was installed on the desktop, and the next day it stopped needing reinstalls.
-
The vault moves into one folder
The whole markdown vault moved into a single folder with its history intact, and the repository root became the app. That one move drew the boundary: the constitution's rules govern the vault, and normal engineering practice governs the code. Markdown stays authoritative, high-frequency events go to append-only logs, and any database is only a rebuildable index.
-
Research before any constant was frozen
Five targeted questions were researched before the first health numbers went into code, without sending any vault content anywhere. The findings set the app's honesty rules: ship an empty personal configuration, never invent a target or a baseline, and label every estimate with its method.
-
A backend that can only write by committing
The backend became the single writer to the vault. Every write is committed together with its reason, and a failed commit restores the file's exact previous bytes. Captures carry an idempotency key, so a retry never writes twice, and every route sits behind a local-access gate that fails closed. The web app's capture lanes queue offline and only report a capture as saved once the backend confirms it.
-
The morning check-in
The Today page shipped with a short morning check-in. Its note and its event log commit together or roll back together. The check-in was kept deliberately light, about fifteen seconds and never a symptom checklist, because consistency matters more than detail.
-
Caffeine, habits and a naming fix
Caffeine tracking shipped with one backend model whose estimates are labelled as estimates, and habits shipped with a visual dashboard and a single implementation of completion and streaks. Building them exposed log filenames that would have collided with other notes, so app-managed logs now carry their domain in the name. The fix landed before any real log existed.
-
The rest of the blueprint in one day
Workouts with a rule-based coach (suggestions only, never auto-applied), nutrition with an import that never overwrites manual entries, lab records, a personal CRM, tasks, a calendar with recurring events and a daily brief all landed. The brief composes what the vault says with each source isolated, so one broken source never blanks the page. Ranking what to do next was left to the agent's judgment on purpose: the app never fakes a priority score.
-
A borrowed design system
The whole front end was restyled onto Meta's Astryx design system. Two of its components failed in real use and were replaced with custom ones the same day. The restyle lasted three weeks.
Brian's call He chose Astryx for the restyle.
-
An installable desktop app
Alfred, then still called Vulcan, shipped as a thin Tauri desktop shell over the web app. The shell starts the backend and ties it to its own lifetime, so closing, crashing or killing the window always stops the backend. The three planning documents the build had run on were distilled into the permanent archive and then deleted.
Brian's call He asked for an installed app with its own icon, modelled on a design he had seen solve the orphaned-backend problem.
-
Relaunch is the update
The first installer froze the backend into the app, so every code change needed a rebuild and a reinstall. The day after installing it, Brian challenged that. The shell now runs the backend straight from the repository's own environment and serves the repository's built front end. Updating the code means relaunching, and a reinstall is only needed when the shell itself changes. The installer shrank to a few megabytes.
Era III · 12 – 29 Aug 2026
One shell, one owner
The app got its own shell, a real component library and an updater that proves its work. Then the project became the only home for the personal side of Brian's tools, and four features with nowhere else to go became its job.
-
The whole codebase mapped
An interactive architecture map and a written project map covering every file were generated and committed. Coverage was checked, not assumed: the map had to account for all 203 files before it counted as done.
-
A real shell, and Astryx retired
The app got a proper shell: a top bar with two sections, Second Brain and Dashboard, and a resizable sidebar for navigation. Styling moved to its own token sheet. Astryx was retired three weeks after it arrived, and the effort already spent on it was not treated as a reason to keep it. Second Brain became a working read-only browser for the whole vault.
Brian's call He asked for the two-section top bar and sidebar, and mid-session widened the scope to a real vault browser.
-
One updater, and staleness proved
Checking before building showed the installed app had been running a frozen two-and-a-half-week-old backend, because a documented update procedure had never been run. A single update script now works out which built pieces have fallen behind, rebuilds only those, restarts the app, and checks the result against a version endpoint the running app reports itself. A hook runs the check after every agent turn. Filing a vault note never triggers a rebuild.
-
A component library, on the app's own colours
The app had no component library: the same card markup was written out 35 times, and a rating scale announced itself as a radio group without behaving like one. shadcn/ui was adopted, which is Radix under the hood, with one hard rule: no shadcn colour name reaches the app, and everything is written in the app's own tokens. The same day the dev backend got a single start/stop owner that cannot leave an orphaned process behind.
Brian's call He asked for a real component library and leaned toward Radix. The scope was this app only.
-
"Is it done?" asked three times
The first pass had only migrated buttons. Finishing the job meant extracting the components the code kept repeating, including 26 copies of an alert that each decided for itself whether a failure was announced. A completion audit, run because Brian kept asking whether it was really finished, found two more gaps. In the end roughly 250 hand-written class strings each resolved to one definition. A sidebar that could vanish got a second fix and regression tests.
Method The completion audit was prompted by Brian's repeated question, which the decision log records as the right instinct.
-
Sole owner of the personal domain
Brian's tools were consolidated, and this project became the only home for the personal side: the only vault and the only life-management features. Four capabilities had been built once elsewhere and never here: finance, a journal, goals and a reading tracker. The agent recommended killing two, deferring one and building one read-only.
Brian's call Build all four. With no other home, killing a feature here would delete it from his whole stack, not move it.
-
Prove the backup restores, before finance
Finance would be the first feature to put third-party financial records into the vault, so a restore drill was made a blocking prerequisite. A fresh clone was restored from the remote and its tree hash matched exactly, with old versions retrievable. The drill became a repeatable script, because a push that "succeeded" is not evidence and only a restore is.
-
Four features ship, and version 0.2.0
Goals got full create, edit, complete and drop. The journal got a write surface whose entries are marked unprocessed until the agent's challenge pass, so nothing typed at the keyboard passes as challenged. Books went in as a reading queue inside the existing media library, and finance as plain markdown tables with deterministic categorisation and no model calls. The sidebar was regrouped into Daily, Body, Record and Direction.
-
The app answers to itself
The shell had been built to stay close to another of Brian's apps so a merge would stay cheap. With that project out of the personal domain, the parity rule had no reason left. Every reference to it was removed from docs, code comments and vault notes. That included dictated captures, which now carry a revision note saying they were edited, when and on whose direction.
Brian's call He was shown the exact sentences in the verbatim captures before they changed and confirmed twice.
-
Alfred is defined, and MDX is declined
Brian described an in-app butler called Alfred, an orb-style presence in a dark, minimal shell. Using a vendor API contradicted a rule already enforced in code, so the conflict was raised before anything was recorded, and Alfred was set to run on a local model only. Everything he is would be a file: his instructions and his memory are markdown notes. MDX was considered and rejected, because it needs a compiler and would fail the rule that every note must read cleanly in a plain text editor.
Brian's call Local model only, after being shown the contradiction.
-
Radix wherever there is behaviour
Asked whether the app was "full Radix", the honest answer was 3 of 10 components. Radix is a behaviour library, so the question was reframed: is anything with real keyboard, focus or ARIA behaviour still hand-rolled? All 32 primitives were mapped against the app. Three migrations followed: select, tooltips and progress. The other candidates were ruled out with stated reasons. One bug only showed up in a live browser: a tooltip with no exit animation never left the screen.
-
Release evidence, without the audit
The release gate's evidence checklist was run on its own: every test suite, a clean build, every route confirmed behind the access gate, no personal data in the offline cache, offline capture observed on the real surface, and layout at phone and desktop widths. Three items could not be verified on that machine, and the log records which and why, not a pass.
Brian's call He declined the full pre-release audit in favour of driving the app himself, and the audit stays his call to restart.
Era IV · 7 Sep – 4 Oct 2026
Alfred
The butler went from a recorded idea to an agent with tools in every part of the app. The interface got a new look, and the project took his name.
-
Tasks, calendar and people, fully editable
After a blueprint the day before, tasks got full add, complete, reopen, update and drop, with dropped items kept rather than erased. Calendar events got ids, editing and deletion, and standing commitments. The personal CRM got create, detail, facts, follow-ups and archiving, never deletion. Birthdays appear in every calendar view straight from people notes and are never copied.
-
Alfred gets hands
Alfred became an in-app agent: a local model running a tool loop whose only effectors are the app's existing vault writers. He cannot touch a file any other way, so each change he makes is a committed edit with a reason that names him. Dropping, deleting and archiving are held as pending actions until Brian confirms, so the no-silent-deletion rule is enforced in code, not in the prompt.
Method Left running unattended with the goal. Every delegated build died at the account's usage limit, so the whole build was done inline.
-
Act, never narrate
In the live check against the real vault, one message produced three committed edits. It also showed the small model describing an action instead of doing it, and getting a weekday wrong. The rules and tool descriptions were tightened, and a reply guard now flags any action the reply claims but no tool call made. Confirm cards name the exact item they will change.
Brian's call The live run against the real vault happened on his go.
-
A Batman re-skin, red then blue
The shell had been described as Batman-dark for weeks without changing. The re-skin moved it to cold graphite with one rationed accent, a new signage typeface and an orb that glows only while Alfred is thinking. Mid-build the accent changed from red to a cowl blue, so red went back to meaning errors only. The light theme had been dead code no one could reach, and it was rebuilt and given a switch.
Brian's call First a monochrome-and-red palette, then "lean into the usual Batman palette" with a blue. The light theme was built, not deleted.
-
Nothing logged is not zero
The nutrition card showed zeros on days with nothing logged, which reads as a recorded zero intake. It now shows a dash.
Brian's call Absent, not zero.
-
Alfred reaches every domain
Alfred got tools for goals, habits, food, training, the check-in, journal, books, finance and lab records, each a thin call into that domain's own writer. The first design had him summon a tool set when a task needed it. On the real local model that went 0 for 4, while offering every tool on every turn went 9 for 9 at about three seconds a turn, so summoning was removed. Vault markdown now renders properly in Goals, Second Brain and his replies, and his note links open the note.
Brian's call He chose summoning first, then took the measured recommendation.
-
A cloud model, opt-in only
Brian asked for Claude and OpenAI as options for Alfred. That contradicted the local-only rule, so the recorded position was quoted back with the strongest case on both sides before anything changed. Local stays the default. A cloud model runs only when a conversation is opted in, the page says plainly that data leaves the machine, and nothing ever falls back to the cloud on its own.
Brian's call A temporary trade of privacy for capability, to be removed once local hardware can carry Alfred.
-
Vulcan becomes Alfred
The app had become the butler, so the project took his name. Three places held data or continuity, not just a name: the desktop profile, the offline capture outbox and the agent's project memory. The profile moves before any window opens, and if it cannot move the shell refuses to start instead of creating an empty one. Stored client state is copied first and deleted only after. History was not rewritten: the decision log and git history still say Vulcan.
Brian's call He decided each part of the rename, including keeping the history as it was.
-
Weekdays resolved by the app, not the model
Asked on a Saturday to schedule something "on Friday", the local model filed it on Tuesday every time, despite a date table in its context. The app now resolves the weekday names Brian actually says and files dates on them. Messages naming several weekdays match each item to its own clause. Measured on a throwaway copy of the vault: 0 of 12 right before the first fix and 12 of 12 after, then 13 of 18 to 18 of 18 for multi-weekday messages.
Method Every model-behaviour change is measured on a disposable clone, never the live vault.