From One Machine to Many: How a Second Machine Exposes Every Hidden Bug
From One Machine to Many: How a Second…
A developer expands his environment to multiple machines, surfacing a series of bugs that single-machine setups never reveal.
The article chronicles a developer expanding his dotfiles repo from a single Mac to two Macs, a Windows desktop, and a Claude Code cloud session over one week (October 6–14), getting every tool to stop "assuming it exists alone." Core work includes unified machine roster management via `machines.json`, deriving `win`/`mac` remote-execution commands, launchd-driven login mirroring, and safely passing a cloud token to a private repo via a single-quoted credential helper. workkit shipped 8 releases in a week, fixing bugs that almost all stemmed from the collapse of the single-machine assumption that "disk contents equal committed contents" — repo lists overwriting each other, reading uncommitted working trees, and Windows CRLF breaking YAML parsing. Suponsa's marketplace landed site-ownership verification, delivery URL validation, and timeout-cancellation rules.
Hidden Bugs in a Multi-Machine Environment: Why Single-Machine Thinking Fails
A developer's dotfiles repo used to serve a single Mac — until he expanded his working environment to two Macs, a Windows desktop, and a Claude Code cloud session. This seemingly simple expansion exposed a fundamental assumption in nearly every tool: they assume they're running on the only machine.
According to the developer's notes on his tech blog (covering October 6–14), the week's main work was getting all his tools to stop "assuming they exist alone." A byproduct of this process was a series of highly instructive bug cases — all sharing one trait: they could never be discovered in a single-machine environment.
Three Machines, One Roster File
The developer started by managing all machines with a committed file, machines.json. Every machine has an id, name, platform, SSH host, user, and public key. A setup script adds the current machine's entry and trusts the keys of all other machines on the Mac.
On top of this roster, two commands — win and mac — serve as remote execution entry points. They're essentially two symlinks to the same bash script, and the invocation name determines the target machine. win git pull executes in the repo clone on the Windows desktop, while mac npm test runs on the other Mac. Changes sync via git, since each target holds a repo clone at the same path.
The roster mechanism immediately exposed its own problems:
- The Mac's id and host are now derived from the Bonjour name (the
.localdomain), ensuring entries match the actual SSH dial name - The Windows scheduled task switched to the S4U logon type, allowing tasks to run without anyone logged in and without storing a password
- An SSH session forwarded the shell's locale to another Mac that lacked it, causing perl to crash. The
maccommand now sends no locale at all
The developer's most-used feature is login mirroring. A launchd task reads Claude Code login info from the Mac keychain every 15 minutes and sends it to the other machines. Windows gets the credential file directly, while the other Mac receives it via a drop folder applied by its own agent. Each target has a digest, and unchanged runs send nothing. One login, all machines follow.
Cloud Sessions and GitHub Access: A Timing Trap
The fourth "machine" is the Claude Code cloud environment. Its setup script clones dotfiles and runs the Claude-related parts of setup, giving the cloud session the same hooks and skills as local.
The problem: the setup script runs before the session's GitHub access is established, and the repo is private. The solution is to have the environment carry a fine-grained token granting read-only Contents permission to that one repo. The most elegant part is how git obtains the token:
git -c 'credential.https://github.com/.helper=!f() { echo username=x-access-token; echo "password=$DOTFILES_CLOUD_TOKEN"; }; f' \
clone https://github.com/<owner>/<repo>.git "$R"
The single quotes are crucial. The command line carries only the variable name, and only the helper's own shell expands it. The token never appears in the process list, never lands on disk, and never reaches .git/config. Pass tokens by name, never by value.
Two rules follow from this: the setup script always exits 0, because a failing setup script would prevent the session from starting; instead it logs to a file. And the plugin repo it clones next has no credentials at all, so it must be public.
The git credential helper mechanism: credential.https://github.com/.helper is a credential provider git configures for a specific URL prefix. When git needs HTTPS authentication, it invokes the helper, reading protocol, host, and other info as key=value from stdin, then reads back the username and password from stdout. In the article, the helper is written as an inline shell function, with f() { ... }; f letting git execute it on demand. The reason the whole config is wrapped in single quotes is that the outer shell won't expand $DOTFILES_CLOUD_TOKEN inside them — only the helper subshell spawned by git will expand it. That's precisely the key to keeping the token out of the parent process's argument list and off disk. If double quotes were used, the token would be substituted into the command line by the parent shell before git even starts, exposing it directly to ps.
workkit: The Second Machine Found the Bugs
workkit is the developer's workflow toolkit for Claude Code, using GitHub issues as a work list, labels as a pipeline, and hooks to enforce rules. This week it shipped 8 releases (0.73.0 to 0.74.4), and nearly every fix was a bug that a single machine couldn't expose.
The repo list problem: The toolkit publishes its managed repo list to the home repo. Each machine's publish replaced the entire list, so the second Mac erased the first's repos. Now the file keeps one entry per machine, and the list is their sorted union.
Disk is not truth: Publishing copies folders from disk. Uncommitted edits on one Mac reached the home repo, then got flipped back when the other Mac published. Now every publish, heal, and workkit doctor check reads the committed tree (HEAD), never the working folder.
The line-ending problem: On Windows, the heal operation appended jobs to checks.yml with CRLF line endings. Reading the file's last key tripped over the trailing carriage return. Now it reads past \r and writes new lines with the file's own line endings.
Commit policy: Local sessions now commit directly to the default branch. Pull requests are left to cloud agents or external contributors.
The common thread running through all these issues: on a single machine, "what's on disk" and "what's committed" look like the same thing. The second machine separates them, and every shortcut that reads from disk becomes a bug.
CRLF vs. LF line endings: Windows uses CRLF (\r\n) as its default line-break sequence, while macOS, Linux, and most git repos use LF (\n). When content generated on Windows is written to a YAML file, the lines carry an extra \r. Many YAML parsers treat a trailing \r as part of the value, so reading "the file's last key" yields an invisible character at the end of the key name or value, causing the match to fail. In a single-machine environment, all files share consistent line endings, so the problem never surfaces; once the same file is read and written by different platforms, it's exposed. The article's approach — "read past \r and write new lines with the file's own line endings" — is essentially maintaining line-ending consistency at the file level rather than forcing everything to one convention.
Suponsa: Prove It First, Then Sell
Suponsa is a marketplace for guest posts and link-insertion placements. This week the marketplace features went live: seller tools, payments, and migration of already-sold placements for the company site. Along with that came three rules:
Prove site ownership: Sellers get a token to place in /.well-known/suponsa-verify.txt or a suponsa-verify meta tag. The file check doesn't follow redirects, and the meta tag check follows one. A domain change on a verified site resets the proof, and a site that's currently for sale can't change its domain at all.
Delivery must have an address: Marking an article as delivered now requires its live URL: https-only, no whitespace, up to 2048 characters. If the publish step rejects the address, the submission stays in paid status and won't be re-published on retry.
Nothing waits forever: Every submission records when it entered its current state. A daily job cancels submissions in submit status for more than 10 days, changes requested for more than 14 days, or approved for more than 7 days, and emails both parties.
CI delivered a humbling lesson. Backend tests spin up the Firebase emulator, and this stack didn't survive five times on hosted runners. Four runs each hit a missing environment (Java 21 among them), and the fifth lost the runner at 48 minutes. For now CI only runs the web suite, while the backend suite runs in the local commit gate on every commit.
.well-known and site ownership verification: .well-known is a reserved path defined by RFC 8615 for site-level metadata, commonly seen in ACME, OAuth, Apple Pay, and similar contexts. The verification service requires the seller to place a random token at https://<domain>/.well-known/suponsa-verify.txt, or add <meta name="suponsa-verify" content="..."> in the page's <head>, then the server makes an HTTP request to check. Not following redirects prevents an attacker from using a 302 to point the verification request at a domain they control; the meta tag check follows one redirect because many sites redirect between www and the bare domain. Resetting the proof after a domain change prevents arbitrage like "buying a verified domain and rebranding."
Small, Satisfying Improvements
Daily posts: The developer started publishing short daily reports on dev.to, studying an open-source project and running it when safe. Covers are rendered by his own cover engine from a JSON plan — never stock images. An API lesson: tags set at upload time aren't replaced by tags in a subsequent front matter edit; you need to send a tags array in the update body.
Status in summaries: workkit summaries now put issue status after the link (like [qa], [closed]), and open issues without a status label show [unlabeled] until someone labels them.
Next week, the cloud environment gets its first scheduled task, and Suponsa's backend CI will try again. If you run tools across multiple machines, which assumption breaks first for you?
Related articles

WorkBuddy Full Tutorial: Build an AI Office Automation Agent from Scratch
A complete WorkBuddy tutorial: installation, three working modes, multimodal picture book video practice, expert team agent building, and workflow configuration for low-cost AI office automation.

WorkBuddy Agent Complete Guide: From Beginner to Automation in 7 Days
Based on Bilibili's 256-episode WorkBuddy masterclass, this systematically breaks down Tencent's AI office workbench core capabilities: Agent principles, Skill encapsulation, cross-platform connectors, scheduled automation, knowledge bases, and zero-code development, with learning path recommendations.

8 AI Accounts, 7 Failed: An Honest Review of Using AI to Get Work Done
Only 1 of 8 AI-run Bilibili accounts succeeded. We break down the real difference between "performing AI" and "using AI to get work done."