JarvisOS
The personal operating system
A private operating system for one person running eight businesses — twenty-seven wings, a few hundred background workers, and the only codebase here whose build journal is longer than the poetry.
- 950 commits
- 2026-05-08 → 2026-08-11
- TypeScript 97.2%
- private repository
The other builds each solve one problem for one audience. JarvisOS solves the problem of running all of them at once: eight brands, a studio, a shop, a clinic of half-finished ideas, and one person holding every thread. Nothing off the shelf models that, because the market for software that runs a one-woman conglomerate is one woman.
So the design constraint was never features. It was attention. A system that needs to be checked is another job; a system that speaks first is an assistant. Almost every architectural decision below is downstream of that — scheduled workers that surface things, a signal bus so one wing can know what another wing saw, and a morning brief that reads the night's output so she does not have to.
4 decisions
What the constraint forced.
Each of these is a choice with a reason attached. The reason is the part worth reading — a stack list tells you what was used, and nothing at all about the judgment that put it there.
- 01
Route the model to the job, not to the best model available.
Body-wing AI — recovery alerts, daily summaries, routine chat — runs on Haiku only. That data is structured and formulaic; a frontier model would spend Sonnet money to reformat a number it was handed. Sonnet is reserved for the work that is actually reasoning: reviewing the day's real commits, mediating a judgment call, writing. Model choice is a cost architecture decision, and treating it as a quality setting is how AI bills get away from people.
- 02
Wings talk through a signal bus, never through each other's tables.
Each wing publishes a small typed payload to a namespaced key and subscribes to the ones it cares about — the body wing emits a recovery score and consumes the cycle phase; the CEO morning brief reads both. Nothing reaches across into another wing's schema. Twenty-seven wings that queried each other directly would be a single wing with twenty-seven names, and the first schema change would take the whole thing down.
jarvis:signals:body:{userId} → { recovery_score, sleep_hours, training_sessions, alert, alert_advice } jarvis:signals:cycle:{userId} → { cycle_phase, cycle_day } - 03
Derive the score in the open, so it can be argued with.
The recovery score is not a model output. It is a clamped sum of three contributions — heart-rate variability against personal baseline, sleep hours against a floor, self-reported quality — and it is written down as arithmetic. A number that tells you how your body is doing has to be inspectable, or you either believe it too much or stop reading it. The version that a model produces is more sophisticated and worth strictly less.
baseline 50 + hrv : (hrv_ms / baseline_hrv - 1) × 25 , clamped ±25 + sleep : (sleep_hours - 6) × 5 , clamped ±15 + quality : (sleep_quality - 5) × 2 , clamped ±10 final = clamp(sum, 0, 100) - 04
Monitor the monitor.
One subscriber to the queue's own `function.failed` event covers every worker in the system at once, including the ones written after it, plus a six-hourly sweep over the heartbeat tables with a latch so a stuck job alerts once rather than forty times. This was not foresight. It was written the week after six workers went quiet for a month and nothing said anything — the scar is in the Scar Room, and this is the fix that came out of it.
The part that survives the build.
A silent failure is worse than a loud one, and much easier to build by accident.
Six background workers resolved the wrong tenant and did nothing successfully for a month. Every one returned 200. Nothing retried, because nothing had failed. The lesson that generalises: a worker whose no-op and whose success look identical to the outside is not monitored, however much monitoring is pointed at it. Make the no-op an event.
Prompt caching is an architecture decision, not a flag.
Caching a large stable system prompt changes what is affordable, which changes what can be built. Once the standing context is nearly free per call, per-user context injection becomes the cheap part and the design moves from "one big careful call" to "many small situated ones". The saving is the headline; the shift in what is designable is the actual result.
Seed data that looks like production data will eventually be read as production data.
A dashboard panel showed five rows of AI review output for weeks. They were seeds — no such review had ever run. Nobody lied; the fixture simply outlived the moment when everyone knew it was a fixture. Seeds need to be visibly fake or deleted before launch, and a panel with no real rows should say so rather than fill itself.
The cost of a system is the cost of understanding it six months later.
The dedup passes in this journal are mostly about a second copy of something — a legacy dashboard over the same tables, a hub page that re-implemented four dedicated pages inside itself. None of it was broken. All of it doubled the cost of every future change, which is the tax that eventually stops a solo build dead.
Built with
- Next.js
- TypeScript
- Supabase / Postgres
- Inngest
- Upstash Redis
- Claude API
- Vercel
Measured
- commits
- 950
- last commit
- 2026-08-11
- journal sections
- 299
- nights logged
- 82
Written from JarvisOS BUILD_JOURNEY.md — the diary, 145 sessions. The dossier is composed from the journal rather than quoted out of it — the raw sections stay behind the review gate described in docs/THE_FORGE.md §4, and every figure above is read from the GitHub API at build time.
