By the time I sit down in the morning, the machine has already put in a shift. A model bench runs against my three resident models around 4:30am. A wiki gardener lints my knowledge base at 5:15. Bookmark capture fires at 7:00, source ingest at 7:30, a doctor at 7:55. An autocommit job commits the night's output at 8:15. At 8:55 a small local model narrates everything that happened and emails it to me. None of this touches a cloud API. It all runs on a 64GB Mac Studio, on Qwen-family models served through MLX and llama.cpp, scheduled as plain cron jobs.
Alongside that chain, five recurring agent lanes read my local corpora and write reports: an action-item extractor, a codebase sentinel, a report QA judge, an opportunity miner, and a knowledge distiller. I built the whole thing to compound knowledge while I sleep. What it taught me instead came from two failures I watched in the logs, and both changed how I build a local pipeline now.
The lanes are deliberately boring
Each lane does the same four things. It reads local sources, calls a local model endpoint, writes a Markdown or JSON report, and stops. The runner will not edit a repo, send a message, open a pull request, or touch source material. That restraint is in the code, not the prose. Before a single prompt goes out, the runner checks the endpoint and refuses anything that is not plain loopback HTTP. A remote address gets rejected on the spot. The lanes are Hermes no-agent jobs, so there is no cloud synthesis model anywhere in the loop. The script only ever talks to a model running on this machine.
Two of the five run weekly, not daily. The opportunity miner runs Mondays, the knowledge distiller Fridays. I kept catching myself calling the whole stack daily, and treating a Tuesday with no miner output as a failure, until I stopped.
Anything a lane surfaces that would have real-world consequences, anything destructive or public or credential-related, stays behind an explicit approval gate and never executes on its own. That gate stands between a small local model's confident suggestion and a change I did not authorize.
When the smallest model invents work
The first failure was the smallest model in the pool. I had a 1.2B extractor in the action-item lane because it is fast and cheap, and on the first reviewer-gated smoke run it turned speaker claims into action items that nobody committed to. Not paraphrase drift. Invention. It read something a person said in passing and produced a task as if it had been assigned. I demoted it to fallback and put the 35B Qwen worker first. The comment I left in the code is blunter than I would be in a meeting: the 1.2B stays a fallback instead of letting the toddler drive the forklift.
That fix exposed a deeper one. Every lane already had a reviewer gate from its first design, so my instinct was that review would catch the fabrication. It mostly did. Before the July hardening, the workers fabricated findings and the whole-report reviewer correctly rejected four of the five lanes. The net useful output was close to zero. I had built an elaborate machine whose most reliable behavior was rejecting its own work.
Two changes brought it back to life. First, the reviewer is a different local model than the worker whenever both are up, because same-model self-review rubber-stamps its own output. Second, and this mattered most, I moved the gate from whole-report to per-finding. One hallucinated finding used to sink a report full of good ones. Now each finding is judged KEEP or REJECT on its own, and a report where some survive and some get cut passes as PARTIAL, so the good findings still reach the queue. The live proof landed the day I shipped it: a knowledge-distiller run kept seven of eight findings and rejected exactly the one that was hallucinated. Seven survived because I stopped letting one bad line kill the batch.
A deterministic filter runs before the model reviewer ever sees a finding. If a finding cites a "Source 4" that does not exist in the grounding pack, or carries a quote that does not appear verbatim in the corpus, it gets dropped mechanically, no model judgment. I would rather a regex catch a fabricated quote than spend a reviewer call on it.
The verdict parser taught me a smaller lesson the same day. Every local endpoint went down, and a reviewer's literal "Verdict: FAIL" got parsed as a pass, because the parser only knew the pass words. Outage wording is now in the fail list, so a FAIL, for any reason including that the reviewer never ran, can no longer launder into a passed gate.
The knowledge base writes itself, then commits itself
The overnight chain builds a knowledge base I maintain in Obsidian. The ingest job used to delegate its file writing to a headless cloud model. I rewrote it so deterministic Python owns every file write and a local Qwen model does one job: summarize each source. That is a real migration, not a local-from-day-one story, and I tell the honest version because the trust boundary moved. When the local summarizer is down or emits invalid JSON, the runner records a WARN and requeues the sources instead of marking weak fallback output as processed. A queued source is recoverable; a silently botched one is not.
For weeks, nothing committed any of this. Cron wrote the vault every morning and the tree accrued uncommitted machine output until a human noticed. So I built an autocommit job that fires at 8:15, after ingest and doctor. It commits under its own distinct identity with a generated-by trailer, and never pushes to a remote. The split inside it is the important part. The staging set is computed deterministically, because a small model must never decide what to commit. The local Qwen writes only the commit message, bounded and stripped, with a deterministic fallback if it is down. The model gets to phrase what happened, not choose what changes.
Nobody re-opens a status file
The second big lesson was not about models. It was about me. I had a verification suite writing STATUS files for months, and I told the team the truth: I know the document exists, but I forget it is there, so I never look at it. I need information brought to me.
That reframed every monitoring system I had. A job that only writes a file is invisible, because the read never happens. So the morning brief became the fix. At 8:55 it gathers every verification STATUS file, plus scheduled-job failures and endpoint and disk probes, has a small local model narrate the result, and emails it to me. The facts are assembled deterministically and the model only phrases them. If every model is down, the email still ships in deterministic form.
The brief carries one card I keep going back and forth on: an estimate of what all this local compute would have cost at cloud API rates. The first reading was about $5.34 over 24 hours and roughly $38 all-time tracked. It mixes measured tokens with documented per-call estimates priced against published API rates, so I read it as an order-of-magnitude signal rather than an accounting figure.
The limitation I will not paper over: the reviewer gates and the fabrication filter reduce the fabrication rate, they do not zero it. A per-finding gate that kept seven of eight is a good day, and a 1-in-8 hallucination surviving on a bad one is still possible. I read the whole stack as a net positive with a floor of local models that no directive can take away, wired through a router I open-sourced as Cerebellum. It is not a system I trust to run unattended forever. It is a shift worker I still check on every morning. The email is how I check.