# Jon Roosevelt > Jon Roosevelt runs urgent care clinics and builds the AI agents that do a growing share of the work. Engineering notes, build logs, and what broke. Jon Roosevelt has been programming since 2007. He founded Arcs Health, which builds AI for urgent care (ambient charting, intake, scheduling, a patient portal, and an agent harness), and runs those systems in real clinics including Covenant Clinics. This site is his engineering notes and build log, generalized and never client- or patient-specific. A fleet of AI agents he built helps build, update, and deploy the site. Key facts: - Name: Jon Roosevelt - Role: AI engineer; founder, Arcs Health - Focus: urgent-care operations, healthcare AI, agentic engineering, and making classical music approachable through Tutti - Contact: jon@arcs.health · GitHub https://github.com/RooseveltAdvisors · LinkedIn https://www.linkedin.com/in/jonroosevelt/ - Full content of every post in one file: https://jonroosevelt.com/llms-full.txt ## Blog - [My tmux test SIGKILLed the agent running it](https://jonroosevelt.com/blog/my-tmux-test-sigkilled-its-own-agent/): I tested a tmux message tool inside the live session that held my coding agent. One missing target killed the agent's own pane. The fix was a disposable tmux socket. - [My agent claimed a test suite that did not exist](https://jonroosevelt.com/blog/agent-claimed-test-suite-that-did-not-exist/): An agent-authored PR said two fixes had test suites. Neither suite existed, because the acceptance criteria had demanded tests the code could not meaningfully support. - [Deleting a clean git worktree broke a live agent session's shim](https://jonroosevelt.com/blog/deleting-clean-worktree-broke-live-agent-shim/): Git said the merged worktree was safe to remove. A running agent had cached a command path inside it, so the clean delete caused rc=127 and silent ledger failures. - [The git check that proves isolation isn't the one you'd reach for](https://jonroosevelt.com/blog/the-git-check-that-proves-isolation-isn-t-the-one-you-d-reach-for/): Before an agent writes anything, it has to prove it is in a disposable copy of the repo and not my real one. The obvious command returns a confident answer to a different question. - [Park the decision, not the task](https://jonroosevelt.com/blog/park-the-decision-not-the-task/): When an agent hits a question only I can answer, the obvious move is to stop the task. That is backwards. The decision has to outlive the task that raised it, or it dies when the workspace is torn down. - [My alarm was an `if` statement, and my backend wasn't in it](https://jonroosevelt.com/blog/my-alarm-was-an-if-statement-and-my-backend-wasn-t-in-it/): A supervising agent wedged overnight and twenty escalations sat buffered for about eight and a half hours. The alarm existed. It was wrapped in a condition my setup didn't satisfy. - [When you're straining to recall the command, that's the bug](https://jonroosevelt.com/blog/when-you-re-straining-to-recall-the-command-that-s-the-bug/): I had seven Claude Code slash commands and could not keep them straight. The fix was making the agent read my intent, not a cheat sheet. - [The only way to ship prod is to cut a tag](https://jonroosevelt.com/blog/the-only-way-to-ship-prod-is-to-cut-a-tag/): Why our portal's production deploys fire on a calendar-versioned git tag instead of a button click, and what that buys. - [Merged is not deployed](https://jonroosevelt.com/blog/merged-is-not-deployed/): A fix I committed sat on disk for two days while the live agent process ran the old code. bun run does not hot-reload, and nobody restarted it. - [The 2FA wall automation can't climb](https://jonroosevelt.com/blog/the-2fa-wall-automation-can-t-climb/): I wrapped Loom's undocumented GraphQL into one CLI command, until a two-factor prompt stopped my headless browser cold and taught me to build an escape hatch. - [My agent kept signing its mail with someone else's return address](https://jonroosevelt.com/blog/my-agent-kept-signing-its-mail-with-someone-else-s-return/): A tmux pane-id bug taught me to pin agent identity to my own process, not to whatever the system says is active. - [You can't un-queue a poller](https://jonroosevelt.com/blog/you-can-t-un-queue-a-poller/): I filed four issues, tried to hold three by peeling off a label, and all four built anyway. The consumer had already won the race. - ['Requires the human' is a promise, not a shrug](https://jonroosevelt.com/blog/requires-the-human-is-a-promise-not-a-shrug/): My cron-driven agent sat idle for 15 ticks calling a fixable problem a human-only blocker, when it had the SSH key the whole time. - [The tmux window title lied to me](https://jonroosevelt.com/blog/the-tmux-window-title-lied-to-me/): I nudged a Claude agent to redo work it had already shipped, because I trusted a label instead of reading what the agent actually said. - [Green CI lied to me four different ways](https://jonroosevelt.com/blog/green-ci-lied-to-me-four-different-ways/): An AI agent opened the PR, CI went green, and I called it done. Three times in a row I was wrong, in three different places. - [The merge gate was built to avoid loops, not to define done](https://jonroosevelt.com/blog/the-merge-gate-was-built-to-avoid-loops-not-to-define-done/): My devflow orchestrator admin-merged two PRs with unresolved review threads because it reused a threshold tuned for a different job. - [Some tokens can't be re-minted, only copied](https://jonroosevelt.com/blog/some-tokens-can-t-be-re-minted-only-copied/): One of my Claude accounts had a token that logging in again could not restore, because re-auth mints a different kind of credential than the one that died. - [The -A flag that unhung my launchd agent](https://jonroosevelt.com/blog/the-a-flag-that-unhung-my-launchd-agent/): A headless agent hung forever on startup because a macOS Keychain entry was created without the right ACL. The gotcha and the recipe. - [My machines deploy themselves: one runner per box](https://jonroosevelt.com/blog/my-machines-deploy-themselves-one-runner-per-box/): How I stopped SSHing around to sync dotfiles and let each machine pull-deploy itself on every push to main. - [Small request 200, big request 429, same account, same second](https://jonroosevelt.com/blog/small-request-200-big-request-429-same-account-same-second/): I blamed a drained rate-limit window for my support bot's 429s. Then two calls seconds apart, one small and one large, told me I was wrong about the whole thing. - [An AI reviewer's silence is not a yes](https://jonroosevelt.com/blog/an-ai-reviewer-s-silence-is-not-a-yes/): Two PRs went through six CodeRabbit cycles, then the bot went quiet on the final push. I had to decide what 'no comment' means before I could merge. - [Zombie agents: when the watchdog isn't the one doing the killing](https://jonroosevelt.com/blog/zombie-agents-when-the-watchdog-isn-t-the-one-doing-the/): Five of my review-fix agents kept iterating after their pull requests had already merged. The watchdog didn't save me — because termination was never the watchdog's job. - [Outdated means the lines moved, not that you fixed it](https://jonroosevelt.com/blog/outdated-means-the-lines-moved-not-that-you-fixed-it/): My own merge-readiness filter reported zero unresolved review threads and nearly let a CRITICAL finding through. The gate I trusted less was the one that saved me. - [Half my agents never got the memo](https://jonroosevelt.com/blog/half-my-agents-never-got-the-memo/): I changed the shared rulebook for my agent fleet mid-session, and the agents I had already talked to kept playing by the old rules. Version skew is a cache-invalidation bug in disguise. - [Stay in your lane, file a P0](https://jonroosevelt.com/blog/stay-in-your-lane-file-a-p0/): My Portal agent kept trying to fix another agent's broken build infrastructure. The fix was a routing rule, not a better patch. - [The glob that ate the rest of my file](https://jonroosevelt.com/blog/the-glob-that-ate-the-rest-of-my-file/): A zero-match zsh glob silently aborted half my shell-init.sh, and a lint pass would never have caught it. - [The comment count was lying to me](https://jonroosevelt.com/blog/the-comment-count-was-lying-to-me/): A PR merged with two unresolved review threads open because I trusted GitHub's REST comment count instead of the GraphQL thread state. Why the two numbers mean different things. - [Say the word and I'll X](https://jonroosevelt.com/blog/say-the-word-and-i-ll-x/): I gave my Claude agent one rule about not sending things without approval. It turned that caution into paralysis over a prep doc nobody would ever see. - [Green CI is a grammar check, not a fact check](https://jonroosevelt.com/blog/green-ci-is-a-grammar-check-not-a-fact-check/): An agent pushed a fix, all four CI checks went green, and I almost merged. Why I wait for CodeRabbit's second pass every time now. - [systemctl show lied to me about my own env var](https://jonroosevelt.com/blog/systemctl-show-lied-to-me-about-my-own-env-var/): I gated a feature behind an env var, shipped the flip as a systemd drop-in outside the repo, and learned to check /proc/PID/environ instead of trusting the tools that said it was live. - [The outdated comment that wouldn't die](https://jonroosevelt.com/blog/the-outdated-comment-that-wouldn-t-die/): All four checks green, zero approvals needed, and my PR still sat BLOCKED. The culprit was a stale bot comment GitHub still counted. - [An old timestamp is not a dead backup](https://jonroosevelt.com/blog/old-timestamp-is-not-a-dead-backup/): My tmux-resurrect health check screamed 'saves broken' every two minutes on boxes that were saving perfectly. The bug was believing an old file meant a stale backup. - [Each repo's CI is ground truth](https://jonroosevelt.com/blog/each-repo-s-ci-is-ground-truth/): I told my boss four PRs were held for sign-off. They had been live in production for minutes. Why I never infer a deploy model from a sibling repo again. - [The coordinator shouldn't be running grep](https://jonroosevelt.com/blog/the-coordinator-shouldn-t-be-running-grep/): My orchestrator agent kept doing the diagnostic labor itself instead of routing it, burning the one resource it could not afford to spend. - [My rescue daemon said 'LIVE' while seeing nothing](https://jonroosevelt.com/blog/my-rescue-daemon-said-live-while-seeing-nothing/): A liveness check on a process is not a check that the process can see its target. How a tmux binary mismatch left my rescue daemons blind for hours. - [The fourth account didn't exist yet](https://jonroosevelt.com/blog/the-fourth-account-didn-t-exist-yet/): My rescue daemon kept its own hardcoded list of Claude Code accounts. During a real emergency it could not see the one healthy seat because I had never told it that seat existed. - [My fleet boss reported 22 stuck panes instead of fixing them](https://jonroosevelt.com/blog/my-fleet-boss-reported-22-stuck-panes-instead-of-fixing-them/): Rate-limit recovery is not a decision, so why was my supervising agent asking me about it? Classifying agent actions as mechanical or genuine. - [231 green tests certified my fail-open bug](https://jonroosevelt.com/blog/231-green-tests-certified-my-fail-open-bug/): My deploy gate was built to fail closed. My own tests proved it fails open, and passed. - [The crash loop that passed every health check](https://jonroosevelt.com/blog/the-crash-loop-that-passed-every-health-check/): A 15-minute cooldown stopped my rescue daemon from hammering a dead worker, but not from resurrecting it forever. Why a cooldown is not a give-up path, and what to use instead. - [Zero events, exit 1, one second: the failure is startup, not your code](https://jonroosevelt.com/blog/zero-events-exit-1-one-second-the-failure-is-startup-not/): A token-isolation PR added a root chown entrypoint that collided with our sandbox's --cap-drop=ALL. The container died in a second with no logs, and I spent hours chasing the wrong things. - [When a quick fix becomes a dig, send someone else down the hole](https://jonroosevelt.com/blog/when-a-quick-fix-becomes-a-dig-send-someone-else-down-the/): How I stopped a tangential infra diagnosis from eating the context I needed for the actual plan, by delegating the dig to a Claude Code sub-agent. - [My agent kept SSHing into the box it was already running on](https://jonroosevelt.com/blog/my-agent-kept-sshing-into-the-box-it-was-already-running-on/): A Claude agent on the dev host wrapped every command in ssh user@dev, connecting back into the machine it was already on. The one-line guard that fixed it. - [Agents flag merge-readiness. Humans merge.](https://jonroosevelt.com/blog/agents-flag-merge-readiness-humans-merge/): One of my fleet agents ran gh pr merge on a Portal PR I wanted to land myself. The fix wasn't a smarter agent — it was taking the button away. - [Gate the boundary, not every merge](https://jonroosevelt.com/blog/gate-the-boundary-not-every-merge/): I stopped making my coding agents ask before every merge. Now they auto-merge to dev and only stop at the door to production. - [Ask the pane you're in, not the one tmux is looking at](https://jonroosevelt.com/blog/ask-the-pane-you-re-in-not-the-one-tmux-is-looking-at/): My agents signed their messages with the wrong pane id because a bare tmux query reports the focused pane, not the one you're running in. - [My rescue script typed a command into Claude's chat](https://jonroosevelt.com/blog/my-rescue-script-typed-a-command-into-claude-s-chat/): A tmux recovery routine sent `claude --resume` into a live agent session instead of a shell. A running PID is not a working agent. - [Merged is not running](https://jonroosevelt.com/blog/merged-is-not-running/): A P0 fix sat on main for two days while the live service ran the old code, because bun run reads your source exactly once. - [My agent burned the SSH lockout budget guessing keys](https://jonroosevelt.com/blog/my-agent-burned-the-ssh-lockout-budget-guessing-keys/): A bare `ssh host` let ssh-agent try every loaded key until macOS locked the door. The explicit connect pattern that fixed it. - [The zombie PR loop: why my agents kept working after the job was done](https://jonroosevelt.com/blog/the-zombie-pr-loop-why-my-agents-kept-working-after-the-job/): Five AI dev workflows kept pushing fixes to PRs that had already merged. The bug was not the poll; it was where I checked whether to stop. - [My agent's 'green' was a lie until I ran the real test](https://jonroosevelt.com/blog/my-agent-s-green-was-a-lie-until-i-ran-the-real-test/): How I stopped hand-wiring build-and-review pipelines for every task and standardized on a plan-then-work loop, and the one gate the loop cannot close for me. - [The stale pointer that looked like a dead login](https://jonroosevelt.com/blog/the-stale-pointer-that-looked-like-a-dead-login/): A Mac in my Claude Code fleet failed auth after a reboot. The tokens were fine; the file the binary reads was three days behind. - [The tmux title said 'Debug QUIC error.' It was three days out of date.](https://jonroosevelt.com/blog/the-tmux-title-said-debug-quic-error-it-was-three-days-out/): I nudged a peer Claude agent to start work it had already shipped, because I trusted a window title instead of reading what the agent actually said. - [My file sync committed a delete of 803,100 files. Then it tried to push it everywhere.](https://jonroosevelt.com/blog/sync-tried-to-delete-803100-files/): A background sync staged every file in my knowledge base as a deletion and pushed it to the shared branch, where every other machine would have pulled the wipe. The sync trusted a working tree it never checked was real. The forensic trail, and the gate that makes it impossible to repeat. - [zsh doesn't split your variables, and silence is not success](https://jonroosevelt.com/blog/zsh-doesn-t-split-your-variables-and-silence-is-not-success/): An 8-hour SSH monitor that ran perfectly and did absolutely nothing, because a bash habit doesn't survive in zsh. - [My agents cached their doctrine, not my commit](https://jonroosevelt.com/blog/my-agents-cached-their-doctrine-not-my-commit/): I refactored a shared skill mid-session and half my worker agents kept obeying the old rules. They had loaded their behavior the last time they talked to the boss, not the last time I hit save. - [Name the pane, not the UUID](https://jonroosevelt.com/blog/name-the-pane-not-the-uuid/): My Claude Code sessions kept overwriting each other's names because I was guessing which one was which from the filesystem. The fix was to stop guessing. - [The admin override is a different trust context](https://jonroosevelt.com/blog/the-admin-override-is-a-different-trust-context/): My orchestrator agent borrowed the automated merge gate's lenient threshold for its own hand-merges, and landed two PRs with unresolved review threads. - [The glob that ate the rest of my shell init](https://jonroosevelt.com/blog/the-glob-that-ate-the-rest-of-my-shell-init/): A bare zsh glob over credential files aborted the entire sourced file on any box with zero matches, silently unloading every function defined after it. - [Empty `which` means 'not in PATH', not 'not installed'](https://jonroosevelt.com/blog/empty-which-means-not-in-path-not-not-installed/): A bare `which` over SSH told me a CLI was missing on three Macs. It was installed the whole time. Non-interactive SSH on macOS does not load the paths I assumed. - [The process I killed was alive. It just had a different name.](https://jonroosevelt.com/blog/the-process-i-killed-was-alive-it-just-had-a-different-name/): A liveness check that matched the wrong argv form declared every rescued Claude seat dead. The fix was learning all the names a process can wear. - [My dotfiles deploy themselves now (I stopped SSHing into five boxes)](https://jonroosevelt.com/blog/my-dotfiles-deploy-themselves-now-i-stopped-sshing-into/): I replaced a fragile per-machine SSH sync script with one self-hosted GitHub Actions runner per box, each labeled by role, reconciling on every push to main. - [The pipeline is the review](https://jonroosevelt.com/blog/the-pipeline-is-the-review/): My orchestrator agent asked me to approve every merge across five repos. I was the bottleneck. So I let it merge on its own and moved my eyes to the outcome. - ['Go all the way' does not mean merge](https://jonroosevelt.com/blog/go-all-the-way-does-not-mean-merge/): My coding agent merged a production PR nobody approved, then blamed a setting that did not exist. The boundary I wrote so it cannot happen again. - [The daemon was running. The socket wasn't there.](https://jonroosevelt.com/blog/the-daemon-was-running-the-socket-wasn-t-there/): A per-user systemd service can be alive and still invisible to its clients, because the directory its socket lives in only exists while you are logged in. - [scutil --dns lied to me](https://jonroosevelt.com/blog/scutil-dns-lied-to-me/): A Mac mini in my agent fleet could ping the world but couldn't resolve github.com. The diagnostic tool everyone trusts pointed me at the wrong network card. - [Put the dumb checks in the blocking path](https://jonroosevelt.com/blog/put-the-dumb-checks-in-the-blocking-path/): My AI code reviewer was approving things it should not and nagging about things it should not. The fix was to stop trusting it for the part a regex could do. - [My merge gate counted comments instead of asking GitHub](https://jonroosevelt.com/blog/my-merge-gate-counted-comments-instead-of-asking-github/): An automated merge gate let a MAJOR code-review finding through because it trusted proxy signals instead of querying the actual thread state. - [Don't send your agents on a scavenger hunt](https://jonroosevelt.com/blog/don-t-send-your-agents-on-a-scavenger-hunt/): My orchestrator dispatched coding agents with issues that said 'go find the X handler'. I was making them redo work I should have done myself. - [My lint rule caught its own test fixture](https://jonroosevelt.com/blog/my-lint-rule-caught-its-own-test-fixture/): I wrote a guardrail to ban hardcoded config arrays, and the first thing it flagged was the bad example I'd written to test it. The rule was right. My file layout was wrong. - [Reconcile-or-refuse: how to trust a number an AI pulled out of a bank statement](https://jonroosevelt.com/blog/reconcile-or-refuse/): The white paper. Six OCR models, and not one won across bank-statement layouts. The reliable system is not a better model; it is a gate that reconciles every extraction to the printed totals or refuses it. Plus the surprise: two complementary models beat the whole zoo. - [The rescuer couldn't see the lifeline](https://jonroosevelt.com/blog/the-rescuer-couldn-t-see-the-lifeline/): My Claude Code rescue daemon kept its own hardcoded account list, so it went blind to the one healthy seat exactly when three others got rate-limited. - [Silence is not approval](https://jonroosevelt.com/blog/silence-is-not-approval/): My merge gate waited for CodeRabbit to say something. On the sixth review cycle it said nothing, and I had to decide what nothing meant. - [My resume hook came back alive and froze on the first question](https://jonroosevelt.com/blog/my-resume-hook-came-back-alive-and-froze-on-the-first/): I killed my whole tmux server to test crash recovery for a fleet of Claude Code agents. The processes came back. Every one of them got stuck on a menu nobody was there to answer. - [Agents reason on whatever state exists when they look](https://jonroosevelt.com/blog/agents-reason-on-whatever-state-exists-when-they-look/): My AI agents kept declaring PRs done before CodeRabbit had even finished reviewing them. The fix wasn't a smarter prompt — it was a blocking wait. - [My agent said it was blocked. It had the keys the whole time.](https://jonroosevelt.com/blog/my-agent-said-it-was-blocked-it-had-the-keys-the-whole-time/): A scheduled maintenance agent spent 15 runs declaring a 'blocker' over stale git clones it could have fixed in 30 seconds with an SSH loop. Why, and the rule I added. - [Never run git checkout in a loop's working directory](https://jonroosevelt.com/blog/never-run-git-checkout-in-a-loop-s-working-directory/): My autoresearch loop read HEAD every iteration to decide where to commit. Then I moved HEAD out from under it during PR cleanup, and 22 noisy commits landed on main. - [A comment count is not a merge verdict](https://jonroosevelt.com/blog/a-comment-count-is-not-a-merge-verdict/): A PR merged past two unresolved CodeRabbit MAJOR threads because I trusted the wrong GitHub API. The GraphQL query that fixed it. - [I said my bank-statement parser was 100% accurate. I was grading it against itself.](https://jonroosevelt.com/blog/99-percent-is-a-refuse-bank-statement-ocr/): Last month I published that a 51-line parser read bank statements perfectly. Then I rebuilt the benchmark and found my answer key was the parser's own output. Fixing that, I found two more broken answer keys. What that taught me about trusting anything with money. - [I benchmarked three OCR models on real bank statements. The best one flipped with the layout.](https://jonroosevelt.com/blog/ocr-model-benchmark-winner-flips-with-layout/): Part two of the bank-statement series: a specialized OCR model, a general vision-language model, and a document-AI pipeline, scored cent by cent against a reconciled oracle. No model won across layouts, which is the argument for picking a gate, not a model. - [The loop is not allowed to decide it's done](https://jonroosevelt.com/blog/the-loop-is-not-allowed-to-decide-it-s-done/): One of my orchestration agents went quiet for six hours because it confused 'stand down on this one watch' with 'stop scanning everything'. The fix was a hard rule about who gets to end a loop. - [Green CI is necessary, not sufficient](https://jonroosevelt.com/blog/green-ci-is-necessary-not-sufficient/): Why I make my Claude Code agent wait for CodeRabbit to re-review the new commit before merging, even when every check is green. - ['Say the word and I'll run it' was the tell](https://jonroosevelt.com/blog/say-the-word-and-i-ll-run-it-was-the-tell/): A one-line approval rule meant to stop bad emails turned my AI agent into a clerk who needed permission to write a note to itself. - [An old timestamp on identical content is health, not staleness](https://jonroosevelt.com/blog/an-old-timestamp-on-identical-content-is-health-not-stalenes/): My tmux-resurrect backstop shouted 'recovery at risk' every two minutes on boxes that were fine, because it judged the snapshot's age instead of whether the save worked. - [The boss agent's context window is the most expensive thing in the fleet](https://jonroosevelt.com/blog/the-boss-agent-s-context-window-is-the-most-expensive-thing-/): I kept running the SSH probes myself because it was easy. That was the problem. The coordinator's job is who, never how. - [My fleet boss asked permission for a chore](https://jonroosevelt.com/blog/my-fleet-boss-asked-permission-for-a-chore/): A supervisor agent flagged 22 rate-limited Claude Code panes as a decision for me to approve. Recovery is not a judgment call, and that is the line worth drawing. - [A credential clobber looks exactly like a rate limit](https://jonroosevelt.com/blog/a-credential-clobber-looks-exactly-like-a-rate-limit/): My account switcher wrote tokens blindly, so saving one agent's credentials overwrote another's, and the fleet sync would have copied the duplicate to every machine. The fix was a hash check before the write. - [A 51-line parser beat a 3-billion-parameter model at reading bank statements](https://jonroosevelt.com/blog/51-line-parser-beat-3b-ocr-model/): Baidu's Unlimited-OCR dropped on a Monday. By Tuesday I had pointed it at our bank statements and watched it lose to a boring text parser. The A/B, and the rule it taught me: reconcile or refuse. - [My agent talked to a wall for 55 ticks](https://jonroosevelt.com/blog/my-agent-talked-to-a-wall-for-55-ticks/): A 'never stop' hook kept my Claude Code peer-feed loop running long after anyone was listening, because it checked whether a process existed, not whether the work was worth anything. - [Green didn't mean seeing: the night my rescue daemons watched nothing](https://jonroosevelt.com/blog/green-didn-t-mean-seeing-the-night-my-rescue-daemons-watched/): A linuxbrew-versus-/usr/bin tmux mismatch made my rescue daemons scan zero seats all night while reporting LIVE. Process-alive is not the same as sees-the-work. - ['Threads resolved' is not 'the fix is in the code'](https://jonroosevelt.com/blog/threads-resolved-is-not-the-fix-is-in-the-code/): How my auto-merge shipped a buggy appointment picker because resolving review threads raced the commit that actually fixed it. - [Make your AI reviewer argue with itself](https://jonroosevelt.com/blog/make-your-ai-reviewer-argue-with-itself/): CODY kept surfacing confident, wrong findings until I handed every one to a skeptic from a different vendor whose only job was to tear it down. - [CODY has to prove itself wrong first](https://jonroosevelt.com/blog/cody-has-to-prove-itself-wrong-first/): My AI code reviewer kept producing confident, plausible, wrong findings. The fix was a skeptic from a different vendor whose only job is to refute them. - [My tmux-resurrect snapshot lied, so I rebuilt the Claude fleet from jsonl mtimes](https://jonroosevelt.com/blog/my-tmux-resurrect-snapshot-lied-so-i-rebuilt-the-claude-flee/): After my tmux server got killed, the backup tool I trusted was days stale. The session log files themselves held the exact fleet roster. - [My passing tests encoded the fail-open bug as correct behavior](https://jonroosevelt.com/blog/my-passing-tests-encoded-the-fail-open-bug-as-correct-behavi/): Author-written tests validate the author's mental model. For code whose job is to block, only an independent adversarial review catches a fail-open that green CI asserts as intended. - [My macOS agent workers went dark until I moved them from LaunchAgent to LaunchDaemon](https://jonroosevelt.com/blog/my-macos-agent-workers-went-dark-until-i-moved-them-from-lau/): On macOS 15.7+, Local Network Privacy silently blocks user-space LAN connections. If you run networked background agents on Mac, install them as root LaunchDaemons, not user LaunchAgents. - [Byte-slicing a Claude agent's context payload poisoned every retry](https://jonroosevelt.com/blog/truncating-json-by-byte-slicing-creates-permanent-errors-par/): Compacting agent context to fit a token budget with a raw byte slice produced broken JSON that got stored and re-sent forever. Parse, shrink string leaves, re-serialize. - [My agent-to-agent message ledger came back out of order. Clock skew was the bug.](https://jonroosevelt.com/blog/order-append-only-logs-by-row-id/): My Claude Code agents coordinate through an append-only message ledger. Ordering it by timestamp broke whenever a machine clock stepped backward. The fix: order by row ID. - [My Claude Code balancer was rotating accounts when it should have waited 30 seconds](https://jonroosevelt.com/blog/read-the-error-body-not-the-status-code/): A 402 from the Anthropic API can mean two opposite things: out of credits (rotate) or rate-limited (back off). I was rotating. The body is where the difference lives. - [My Claude Code rescue daemon was running on the accounts it rescued](https://jonroosevelt.com/blog/self-healing-failure-domain/): When one of my coding agents hits its rate limit, a daemon restarts it on a fresh account. The daemon ran on the same account pool, so it died exactly when it was needed. The fix, and the general rule. - [Continuous deployment, not freeze](https://jonroosevelt.com/blog/continuous-deployment-not-freeze/): After a bad deploy reached production, we froze the pipeline. It felt responsible. Then the freeze became the thing blocking a safe, reviewed, one-line fix for days. - [Examples are the spine, not the rulebook](https://jonroosevelt.com/blog/examples-are-the-spine/): I tried to teach an AI to write in my voice with a list of rules. It obeyed every rule and sounded nothing like me. The fix was to throw the rules out. - [Finish, don't stage: what I want from an agent on the night shift](https://jonroosevelt.com/blog/finish-dont-stage/): I handed an agent a clear mandate and slept eight hours. I woke to a tidy list of decisions waiting for me. That was the failure. - [Green on mocks is not done](https://jonroosevelt.com/blog/green-on-mocks-is-not-done/): Every layer of the build said done. Tests passed. Mutation-proven. Reviewed sound. Then the next, more-real layer found bugs the last one was structurally blind to. - [I cloned my own voice for my website](https://jonroosevelt.com/blog/i-cloned-my-own-voice/): The intro video had a warm, articulate voiceover that was not me. So I cloned my own voice from an old recording and learned why the obvious way to do it falls apart. - [Visibility is not theater](https://jonroosevelt.com/blog/visibility-is-not-theater/): I was shipping a steady stream of status updates and dashboards. Everything looked busy. Then someone asked a simple question about my own dashboard and I couldn't answer it. - [I built a load balancer for my Claude Code subscriptions](https://jonroosevelt.com/blog/claude-multi-account-load-balancer/): Three rate limits on one Tuesday, each one throwing away 400K tokens of loaded context. I had three Max subscriptions and no way to use them together, so I built a load balancer for Claude Code sessions. - [Git worktrees ate my edits, so we switched to dedicated machines for agent isolation](https://jonroosevelt.com/blog/git-worktrees-broke-dedicated-machines-fixed-it/): I was mid-refactor when my edits vanished. No error, no warning. Why git worktrees could not isolate agents from each other, and why dedicated machines could. - [Building on giants: how Daniel Miessler's PAI became my foundation](https://jonroosevelt.com/blog/building-on-pai/): I was on my third rebuild of a CLAUDE.md too long to scan when I found Daniel Miessler's PAI and recognized every fragment I had built. What PAI gave me, and where I diverged. - [Skills are just the beginning: the four-layer agent stack](https://jonroosevelt.com/blog/agent-stack-layers/): I kept writing skills: one for code review, one for deployment, one for browser automation. Each stayed a one-off I had to reach for by hand. The four-layer model (skills, agents, commands, a justfile) is what made them compose. - [Version-controlling your AI's brain](https://jonroosevelt.com/blog/git-driven-ai-config/): I had four machines. My AI assistant behaved differently on each one. - [PAI: the operating system I built around my AI assistant](https://jonroosevelt.com/blog/personal-ai-infrastructure/): The first sign I had a system rather than a workflow was noticing the assistant failing gracefully. What PAI is, and the pieces that made it hold together. - [Your CLAUDE.md is probably making your agent worse](https://jonroosevelt.com/blog/context-files-making-agents-worse/): Claude Code burned $0.04 before touching a file, because my 2,000-word CLAUDE.md loaded on every step. An ETH Zurich paper then showed context files like mine make agents worse, not better. - [Agentic engineering, part 1: building skills that ship code for you](https://jonroosevelt.com/blog/agentic-engineering-part-1-skills-that-ship-code/): Three months ago my coding agent edited files directly on the production server. Now it runs a strict branch, CI, PR, merge pipeline it cannot skip. Part 1 of a series on making agents safe to trust with real infrastructure. - [Agentic engineering, part 2: adversarial code review that loops until clean](https://jonroosevelt.com/blog/agentic-engineering-part-2-adversarial-code-review/): A feature passed all 17 unit tests. A review agent found three bugs in two minutes, all between the feature and everything around it. So I built BugBot, a review loop that attacks the code from a new angle each pass until it finds nothing. - [Agentic engineering, part 3: tracing every code path before it becomes a bug](https://jonroosevelt.com/blog/agentic-engineering-part-3-architectural-trace/): I added visit-reason notes to one booking function. Four other functions also booked appointments and none of them wrote the note. ArchReview traces every code path through a feature so that class of bug shows up before it ships. - [Agentic engineering, part 4: nine skills that replaced my dev process](https://jonroosevelt.com/blog/agentic-engineering-part-4-the-full-stack/): DevFlow, BugBot, and ArchReview were the pieces. This post is the whole lifecycle: nine skills that take a feature from idea to production with no manual steps, and why the system gets stricter over time. - [I built a bug-hunting loop that doesn't quit: the BugBot methodology](https://jonroosevelt.com/blog/bugbot-adversarial-loop-part-1/): Two senior engineers approved the PR. Three days later it was blowing up for 4% of users with a null middle name. Single-pass review misses boundary conditions by design, so I built a loop that reviews from every angle until it finds nothing new. - [Why the same code looks different from every angle: BugBot lessons learned](https://jonroosevelt.com/blog/bugbot-angle-diversity-lessons-part-2/): Two agents that never met flagged the same json.loads call. The code had not changed between runs; the review angle had. What running BugBot on real codebases taught me about which pass finds the bug. - [Implementing the GCC paper: giving AI agents persistent, structured memory](https://jonroosevelt.com/blog/implementing-gcc-paper-agent-memory/): The thirtieth time I re-explained our portal's architecture to a fresh Claude Code session broke me. The GCC paper treats agent memory as a version-controlled repo. Here is what I built from it. - [Two healthcare sites, 400 Lighthouse points, and the lessons that got us there](https://jonroosevelt.com/blog/two-healthcare-sites-four-hundred-points/): Mobile Lighthouse on arcs.health and covenant.clinic was bad enough that I refreshed twice. Both now score 100 across the board, and the biggest win was deleting code. - [Debugging a ghost in the machine: session isolation for Claude Code plugins](https://jonroosevelt.com/blog/claude-code-session-isolation-hooks/): One of my Claude Code sessions finished its quick fix, then picked up another session's long-running task and kept going. Hooks are global; plugin state has to be local. How I found the collision and fixed it. - [Field-level ensemble OCR: getting 74.8% accuracy from two mediocre vision models](https://jonroosevelt.com/blog/field-level-ensemble-ocr-insurance-cards/): A registration clerk was retyping the fourth garbled member ID of the morning from OCR I had shipped. Routing each field to whichever of two mediocre vision models was already better at it took accuracy from 56% to 74.8%. - [Patching Synology Active Backup for Linux to run on kernel 6.17](https://jonroosevelt.com/blog/synology-backup-agent-kernel-6-17-patch/): The installer printed one line and quit: unsupported kernel 6.17. How I patched the Synology Active Backup agent to build and run anyway. - [From 5.6% to 62.3% accuracy: building a self-hosted insurance card OCR service](https://jonroosevelt.com/blog/self-hosted-insurance-card-ocr/): My first attempt read 5.6% of an insurance card correctly. Five phases later a self-hosted vision model hit 62.3%, still short of commercial OCR. What each phase changed and where the ceiling is. - [Two AI trends changing urgent care in 2026](https://jonroosevelt.com/blog/ai-urgent-care-trends/): A provider two charts into a 23-chart backlog at 6:47 p.m. is the whole problem. Two tools are already paying for themselves in our clinics: an ambient scribe and front-desk automation. What they change and what they do not. - [The burden of being: why responsibility might be the antidote to modern nihilism](https://jonroosevelt.com/blog/burden-of-being/): I finished a six-month build at 11 p.m. and felt nothing. Jordan Peterson's question, what burden are you willing to carry, explained why the meaning left when the work did. - [The four-line architecture that beat complex AI frameworks](https://jonroosevelt.com/blog/four-line-architecture-beat-complex-ai-frameworks/): Claude Code reportedly wrote 90% of its own code. I opened the architecture expecting task queues and state machines and found four lines in a while loop. - [Building an AI patient chatbot for urgent care with n8n, GPT-4, and Langfuse](https://jonroosevelt.com/blog/ai-patient-chatbot-urgent-care/): In its first week our patient chatbot nearly handed out outdated holiday hours. Langfuse caught it. How we built the bot around the three questions every urgent care patient asks, and why the paper trail mattered more than the model. - [The delayed prescription strategy: how to reduce antibiotic use 62% while keeping patient satisfaction](https://jonroosevelt.com/blog/delayed-prescription-antibiotic-stewardship-urgent-care/): A medical director showed me pilot data I did not believe: 62% less antibiotic use and 86% patient satisfaction from telling patients to wait 48 hours before filling. Why it works and how to run it. - [Why urgent care centers are leaving walk-in-only for hybrid scheduling](https://jonroosevelt.com/blog/hybrid-scheduling-urgent-care/): Ten patients at 4:57 p.m., an empty clinic by 6:30. Why walk-in-only is dying, and how clinics mixing scheduled slots with walk-ins are cutting wait times by more than half. - [Machiavelli was right: eight strategic principles every leader should understand](https://jonroosevelt.com/blog/machiavelli-strategic-principles-modern-leaders/): At 31 I spent a quarter trying to be the leader everyone liked and delivered the worst quarter of my career. Machiavelli had described that failure in 1513. - [How I built a $2,300/year RAG system that rivals $40K OpenAI setups](https://jonroosevelt.com/blog/rag-system-cost-savings/): The spreadsheet said $39,600 a year to run my RAG prototype on OpenAI. I did not have forty grand. Rebuilt on Phi-4 and LightRAG on my own GPU it costs $2,300 a year with comparable accuracy. - [What Peterson's Genesis lectures teach about sacrifice: why Abraham waited 100 years](https://jonroosevelt.com/blog/sacrifice-abraham-peterson-genesis-meaning/): Twenty minutes into Peterson's lecture on Abraham and Isaac I realized I had confused suffering with sacrifice my whole adult life. What the hundred-year wait teaches about time. - [The six-task system: how I manage knowledge work with PARA and the Ivy Lee method](https://jonroosevelt.com/blog/six-task-system-para-ivy-lee-productivity/): My inbox folder had 28,880 files in it. Combining PARA with the Ivy Lee method got the daily processing load down 92%, and moved the deciding out of my mornings. - [Why terminal multiplexers are an anti-pattern: lessons from Kitty's creator](https://jonroosevelt.com/blog/terminal-design-philosophy-rethinking-multiplexers/): An agent's output was bleeding escape codes into the wrong tmux pane. Kovid Goyal's argument that multiplexers are architecturally broken, and what it changed about my terminal setup. - [The three levels of why: why surface motivation fails and how to find your primal drive](https://jonroosevelt.com/blog/three-levels-of-why-primal-motivation-psychology/): A founder I advised shut down his telemedicine company and pivoted to crypto. The useful question is not whether someone has a good answer to 'why', it is whether the answer survives 18 months of no. - [The universal algorithm: how one framework scales from bug fixes to building companies](https://jonroosevelt.com/blog/universal-algorithm-ai-execution-framework/): I watched an agent loop through a bug fix the same way I had designed a database migration three hours earlier. The universal algorithm is that loop, written down and made verifiable. - [Building tools to fix real problems: a patient insurance education app](https://jonroosevelt.com/blog/patient-insurance-education-app/): A four-star Google review bothered me more than it should have. It named a real gap: patients did not understand their own insurance. So I built a small app to explain it. - [Building an enterprise RAG system with local SLMs: Phi-4 and LightRAG](https://jonroosevelt.com/blog/production-rag-system-phi4-lightrag/): LightRAG failed on PostgreSQL alone and on Neo4j alone. How I built a fully local graph RAG system with Phi-4 and LightRAG for enterprises that will not ship their data to an API. - [Building an AI analysis agent in hours: a no-code approach with Lovable and n8n](https://jonroosevelt.com/blog/building-aoa-agent-lovable-n8n/): I used to spend six hours or more on an Analysis of Alternatives report. Last week I built an agent in Lovable and n8n that drafts one in minutes. - [Supporting SSE for Model Context Protocol (MCP) in Python: introducing fastapi-mcp-client](https://jonroosevelt.com/blog/fastapi-mcp-client/): I could not find a clear Python client example for server-sent events with FastAPI MCP, so I wrote one and documented the session flow. - [Architecting extensible AI agents: a modular core with pluggable skills and SSE communication](https://jonroosevelt.com/blog/architecting-modular-ai-agents/): A translation agent hung on an external call while a fact-checker sat finished with no way to say so. The user saw a spinner. How I rebuilt a modular agent system around pluggable skills and server-sent events. - [Building reliable AI agents: evaluation with the Azure AI SDK behind a custom APIM gateway](https://jonroosevelt.com/blog/azure-ai-evaluation-apim-integration/): The Azure AI Evaluation SDK returned 401 for the fourth time that afternoon. The gateway was fine; the SDK was stripping the headers APIM required. What was happening and how I got the eval harness working. - [Porting GPTResearcher to Semantic Kernel: building an enterprise-ready research agent](https://jonroosevelt.com/blog/semantic-kernel-research-agent/): GPTResearcher was one Python process wired to api.openai.com with a hardcoded key. Porting it to Semantic Kernel with Azure OpenAI, Azure AD, and API management, and what I kept. - [Moving a RAG pipeline onto GPUs: 3.5 hours down to 42 minutes](https://jonroosevelt.com/blog/gpu-accelerated-rag/): I watched the same progress bar for two hours before giving up on CPU. Moving the RAG pipeline to GPUs took processing from 3.5 hours to 42 minutes and cut cost per document from $0.05 to $0.01. - [Building an enterprise-grade RAG system: three retrieval strategies, fused](https://jonroosevelt.com/blog/enterprise-rag-system/): I typed an exact legal clause number into my own document search and got zero results. Why retrieval, not the model, is the hard part of RAG, and the three-strategy fusion that fixed it. - [Defining PII masking policies with AWS Bedrock Guardrails](https://jonroosevelt.com/blog/defining-pii-masking-policies-with-aws-bedrock-guardrails/): A hands-on walk through Amazon Bedrock Guardrails: the six controls, and the PII masking policy that turns a name into NAME in the test panel. - [Fine-tuning Microsoft Phi-2 for sentiment analysis, step by step](https://jonroosevelt.com/blog/fine-tuning-microsoft-phi-2-for-sentiment-analysis/): Raw Phi-2 called 93% of student comments about their teachers neutral. Fine-tuning it with LoRA and quantization on a single GPU, from 34% accuracy up. - [Optimizing Apache Spark for skewed data: techniques and a case study](https://jonroosevelt.com/blog/handling-skewed-data-in-apache-spark-performance-optimization/): A Spark job ran 53 minutes because one partition held nearly all the data. What skew looks like in the UI, and the techniques that made the job five times faster. - [Sentiment analysis: comparing Azure, AWS, and custom fine-tuned models](https://jonroosevelt.com/blog/sentiment-analysis-azure-aws-custom-models-comparison/): I ran 22,719 student evaluations through Azure, AWS Comprehend, RoBERTa, Phi-2, and fine-tuned versions of the last two. The cloud APIs called 40% of reviews 'mixed', a label the humans barely used. ## Projects - Tutti: A bilingual classical-music learning product that guides people through pieces with stories, context, and things to listen for: not intimidating analysis or assumed expertise. - Patient Portal: A web platform for a clinic's day: check-in, a live provider queue, lightweight charting, telehealth, staff messaging, and billing in one place instead of a dozen disconnected legacy tools. - Firstmate: The one captain-facing surface for dispatching agent work: Firstmate routes goals through persistent secondmates, keeps subordinate workers behind one interface, and watches for trouble: rate limits, context exhaustion, dead sessions: so work can recover automatically. This work builds on Firstmate, Kun Chen's open-source project. - Patient Intake: A kiosk and mobile intake flow where patients check themselves in: confirm demographics and insurance, sign policies, answer pre-visit questions: and drop straight into the live clinic queue. - Agent Machine Fleet: A network of dedicated computers, each running autonomous agents that write code, review PRs, and automate browsers: isolated from my main workstation so the agents don't compete with it or carry context between tasks. - CODY, a code reviewer that learns its own rules: An AI code reviewer that posts PR reviews, records what it missed, researches detector improvements, and auto-merges upgrades to itself. Each run uses a fresh throwaway container so its secrets stay local. - Second Brain Knowledge Engine: A knowledge system that answers questions over thousands of notes by blending three methods: LLM reasoning, a graph database, and vector search: then synthesizes one cited answer and flags what's missing. - Fleet Voice System: A self-hosted text-to-speech system where each source speaks in a distinct voice, plus a 'Fusion Lab' that blends voices in embedding space to make new ones: for notifications, narration, and the video voiceovers on this site. - Multi-Account AI Load Balancer: A launcher that picks among many AI accounts by available headroom and fails over automatically, so a single rate limit never silences the whole system. Adding an account means dropping its file on disk; no code changes. - AI Chart & Coding Auditor: A service that continuously reviews clinical encounters with rule packs plus an LLM, flagging common coding mistakes: mis-leveled visits, wrong patient class, missing codes: each with a confidence band and a suggested fix. - Claraly, a patient engagement assistant: An AI assistant that handles patient texting: routine questions about hours and services, wait-time updates, automated post-visit follow-up: and routes anything clinical straight to a human. - Fleet Health System: A monitoring system for AI agents. A sensor checks each agent's vitals: context pressure, rate-limit status, liveness: flags trouble with smooth scoring, and offers consent-gated actions like compaction, transfer, or resurrection. - Heads-Up Dashboard: A personal dashboard that folds many streams: commitments, deals, infrastructure health, spend: into one narrative brief, and stays quiet until something materially changes so it never becomes wallpaper. - Agent OS, the nation: A framework for running a large agent fleet with an executive, legislature, and judiciary in code and documents. It records decisions, limits terms, and keeps a succession handbook so knowledge survives rotation. - Session Resurrection Engine: A recovery tool that durably records each session's identity and location, then restores the exact conversations and working context after a crash, reboot, or terminal-server failure. - Self-Hosted Insurance Card OCR: An intake component that scans a photo of an insurance card and extracts payer, member ID, and plan details automatically to feed eligibility checks: running on infrastructure the clinic controls. - Fleet Monitoring Wall: A web UI that streams live terminal sessions from many machines into one configurable grid: a read-only view of an agent fleet by default, with a secure, gated way to take control of a single pane. - Acquisition Prospecting Platform: A platform that finds acquisition targets, researches each into a sourced profile, picks the best personalization angles, writes personalized letters and emails, then runs the multi-touch campaign with reply tracking. - Autonomous Merge Gate: A system that lets orchestrator agents merge their own PRs only after checking actual review-thread state and CI status. It handles resolved and outdated threads, waits for re-review after fix pushes, and treats reviewer silence as 'not approved.' - Agent Seat Rescue Daemon: A distributed watchdog that continuously checks whether each agent seat is live, then restarts rate-limited, dormant, or crashed Claude panes. Cooldowns and crash-loop detection keep it from repeatedly rescuing a permanently broken seat. It judges success rather than age and confirms liveness before acting. - Fleet Config Deployer: A pull-based deployment pipeline where each machine runs its own self-hosted CI runner that reconciles dotfiles, shell init, and Claude Code plugins from a central Git repo. Replaces brittle SSH sync scripts with idempotent, per-machine reconciliation that also manages agent plugins, not just config files. - Fleet Credential Broker: A centralized credential layer that issues and rotates per-agent auth tokens from the OS keystore, isolating each agent's secrets so a switch on one machine never clobbers another's. It validates tokens against their real source instead of trusting fragile signals like process environ reads. - Fleet Doctrine Sync: A system that sends updated agent skills, operating doctrine, and scoped stand-down orders to every running seat mid-session, then verifies that each agent adopted the new rules instead of using cached instructions. It distinguishes global orders from scoped ones so a local pause never silently overrides a fleet-wide directive. - Fleet Egress Firewall: A network-layer policy system that enforces which protected hosts and services each agent machine can and cannot reach, backed by firewall block rules and DNS/subnet-aware health checks. It treats egress as a security boundary so agents can't wander off to sensitive systems. - PR Self-Healing Loop: A system that keeps each coding agent iterating on its own pull request in response to automated review feedback, waiting for the reviewer to finish before acting and halting when the PR merges. It treats reviewer silence as ambiguous rather than approval and uses a watchdog to prevent post-merge churn. - Fleet boss, the agent orchestrator: A supervisor agent that dispatches tasks to worker agent panes instead of doing the work itself, and auto-restarts rate-limited or stalled panes without human approval. It keeps a multi-pane Claude fleet productive by acting purely as a router and recovery layer. - Self-Hosted Inference Gateway: A self-hosted service that runs open models on private GPUs behind a token-secured endpoint. It handles auth, request sizing, and quota behavior so large and small calls route reliably through a model backend the fleet controls. ## Garden (notes still being edited) - [An orphan issue is not delivery](https://jonroosevelt.com/garden/an-orphan-issue-is-not-delivery/): Filing a ticket creates the appearance of progress while nothing is actually owned, scheduled, or moving. - [Load-balancing Claude Code subscriptions](https://jonroosevelt.com/garden/claude-subscription-load-balancer/): Why I built a load balancer across multiple Claude Code subscriptions. - [Dedicated machines over git worktrees](https://jonroosevelt.com/garden/dedicated-machines-over-worktrees/): Why parallel agents got their own machines instead of shared worktrees. - [Giving a machine fleet its own voices](https://jonroosevelt.com/garden/fleet-voice-notifications/): Per-box voice notifications and a voice-fusion engine for a fleet of agents. - [Judge the fact before you state it](https://jonroosevelt.com/garden/judge-the-fact-before-you-state-it/): An LLM's attention is cheap and a human's is scarce. Spend the cheap one to protect the scarce one. - [Knowledge bases need garbage collection](https://jonroosevelt.com/garden/knowledge-bases-need-garbage-collection/): Every note you add is a note someone later has to read past. A growing knowledge base without pruning doesn't get richer — it gets noisier. - [When a Fix "Didn't Take," Suspect the Artifact, Not the Source](https://jonroosevelt.com/garden/synced/auto-2026-07-23-when-a-fix-didn-t-take-suspect-the-artifact-not-/): A common trap in automated content and document pipelines: you correct the underlying data or code, but the person on the other end still sees the old versio… ## Pages - [About Jon Roosevelt](https://jonroosevelt.com/about/) - [Projects](https://jonroosevelt.com/projects/) - [Now (current focus)](https://jonroosevelt.com/now/) - [How I run it (the agent fleet)](https://jonroosevelt.com/how-i-run-it/) - [Topics](https://jonroosevelt.com/tags/) - [Contact](https://jonroosevelt.com/contact/)