Skip to main content
WritingSpeakingCodeAboutNow

My business lives in my vault now

My clients, hours, invoices and articles moved out of FreeAgent, Upwork and my head into plain files in my Obsidian vault, kept by an agent, with Tabstack finding the next client.

On a Monday morning my freelance business was spread across FreeAgent, Upwork, a client’s blog and my memory. By the afternoon it was a folder of Markdown files inside my Obsidian vault, with the history filled in, and a script was looking for the next client and writing what it found into the same vault.

None of the tools involved are new to this site. How my brain works covers the vault, My clients live in a folder now covers mavis, and I have written about Tabstack twice. This is about what happened when I pointed all three at the real thing, because that is where each of them turned out to be wrong about something.

The gap in the vault

The vault was already the system of record for everything I build, write and record. Every project has a note, every talk has a note, research gets its own directories. The one part of my working life that was not in it was the part that pays for the rest.

That was deliberate while mavis was being built. I did not want my real records to be the test bed for an unfinished tool, so mavis ran against a throwaway folder and the vault was left alone. Once it was stable, moving in took one line of config:

root: /home/steve/brain/business

mavis keeps its default layout under that folder: clients/, engagements/, log/, time/, invoices/ and quotes/, with generated PDFs in a dot-directory so Obsidian skips them. I kept it as one folder rather than spreading clients and timesheets across the vault’s existing sections, because then mavis’s root is its own and nothing of the vault’s sits inside it.

The part I had not planned was how well it lines up with a rule the vault already had. Every project note carries a client: field, and the conventions say that when a client accrues enough work to want a home, it gets a note, not a folder. A mavis client record is exactly that note: contacts, status, rates, history, and anything I write in it by hand survives. The short name in a project’s client: is now the file name of the client’s record.

Filling in the history

An empty system is useless for the question I actually care about, which is how the business is doing. So the first job was the back history, and it came in three different shapes.

Articles. One client pays per article, and I have been writing for them for a long time. Their blog is the record, so I read it: the sitemap lists every post, each post names its author in a meta tag, and a short script turned every one of them into a note in the vault, holding the full text as published. A second pass found the earliest pieces were not on their blog at all. They were sponsored posts on this site, which a sponsor: field I had since tidied out of the frontmatter still identified in git history.

Hours. Another client runs through Upwork, which keeps a weekly record. Nineteen weeks went in as nineteen entries, each dated the Sunday the week ended. They added up to two hours short of the total I had in my head, and I went with the weekly figures, since those are what got billed.

Invoices. The third client is invoiced weekly through FreeAgent, so the history was a list of amounts. Every amount divided cleanly into days at one of two day rates, the lower one before mid-May and the higher one after. That looked like a rate rise, mavis had no way to model one, and I built it: an engagement now keeps its earlier rates, and every hour is valued at the rate on its own date.

Then it turned out not to be a rate rise. In May I had moved from one project for that client to another, at a different rate. The fix was to split the history across two engagements, and the rate history I had just built went unused. I kept it anyway, because rates do change, and the next time one does it will already be there.

That was the pattern for the whole day.

Real data breaks the model, usefully

mavis shipped three releases that afternoon, each because my own records did not fit it.

The per-article client did not fit any billing basis it had: day, hourly, fixed or retainer. Per-item work is now a basis of its own, with a unit, and deliveries live in the same monthly sheet as time:

Terminal window
mavis delivered acme-articles "Laravel queues, a deep dive"

Then every client I added turned out to be invoiced somewhere else. One through FreeAgent, one through Upwork and reconciled in FreeAgent, and the per-article client through FreeAgent too. For a tool whose invoicing is the part with the most rules, that is an awkward thing to find out about your own business. It is also exactly the case where a second system numbering invoices for the same client causes trouble, so a client can now say where it is invoiced, and mavis refuses to draft anything for it:

---
type: client
name: Acme Ltd
status: active
invoicing: FreeAgent
---

The rate history was the third. None of the three were on the plan a week earlier. All of them came from importing records I already had, which is a better test than any sample data I could have written.

Who does the typing

I did very little of this by hand. mavis runs as an MCP server, and the repository is a Claude Code plugin, so most of the day was me saying things like “Mozilla is billed hourly in dollars, starting on the 28th of May” and an agent making the change. Everything it creates is stamped by: agent.

The line from the mavis article held up. The agent can keep the records and draft, but it cannot issue an invoice or mark one paid, because those tools do not exist on the server. And the vault’s own rule meant every change landed as a commit I could read before it went anywhere: when I explained how one client tends to work with me, a follow-up moved and the client’s note gained a line about it, which the next session reads before it suggests anything.

Tabstack looks for the next one

The last piece closes a loop. Knowing who you work for is half of it. The other half is knowing who you might work for next.

I should be clear about my interest here: Tabstack is the product I work on for Mozilla. It is also the right tool for this, because the job is web research with sources, and structured extraction from a company’s own pages.

The script is small and lives in the vault, under .tools/prospect, because its only job is to write into the vault. It runs a research query for each of three signals: developer tool startups that have just raised money, small companies launching a public API or SDK, and companies that pay external writers for their own blog. It asks for one company per line, reads them back, drops anyone already in mavis or found by an earlier run, and then checks each one against its own homepage:

const p = Bun.spawn(
["tabstack", "extract", "json", `https://${domain}`, "--schema", `@${join(here, "qualify.schema.json")}`, "--output", "json"],
{ stdout: "pipe", stderr: "pipe" },
);

The schema asks whether the company sells to developers, whether it has a public API or SDK, and where its docs, blog, careers and contact pages are. Fit for each kind of work I do follows from those answers. Each run becomes a dated finding in a research directory, and nobody goes into mavis until I pick them.

The homepage check earned its place on the first run. The research named a recently funded startup and gave its domain, and the check came back with “This domain is for sale”. The research had the company right and the website wrong, and without the check that lead would have looked as good as any other.

The rest of the first day was tuning:

  • An open search for API launches returned Google, OpenAI and Apple. Asking for small companies helped, but the query alone did not hold the line, so the script now has an exclude list that drops big company domains whatever the research says.
  • The writing search first returned publications that take free guest posts. Asking specifically for companies that pay for their own blog turned it into the most useful search of the three, mostly through community-maintained lists of paid writer programmes.
  • My plan only allows Tabstack’s fast research mode, which I found out when the first real run failed every search and then wrote an empty finding anyway. It now writes nothing when nothing worked.

Two runs produced fourteen leads. Five of them are now prospects in mavis.

What the vault is for

The tools are the least interesting part of this. mavis could be any tool that keeps records as plain files, and the prospecting script would work with any search API that cites its sources.

What matters is that the business now lives in the same place as everything else, under the same rules. A client’s record links to the projects I do for them. An article I wrote for a client sits beside the ones I wrote for myself, at a different status. A lead links to the research that found it. When an agent picks up any of it next week, it starts from the vault, and the vault is right.

Share

XLinkedIn

Related

Keep Reading

All posts →