DEV Community

Cover image for A second machine finds every bug the first one hid
Ian
Ian

Posted on

A second machine finds every bug the first one hid

This one covers October 6 to 14. Most of the week went into a change that sounds small: my setup stopped being one Mac. It is now two Macs and a Windows desktop, plus a cloud session, and every tool I own had to stop assuming it was alone. The rest went into my workflow kit catching up, a marketplace build, and a new daily post.

Three machines, one roster file

My dotfiles repo (private, so no link) sets up a whole machine with one script. Until this week, "the machine" meant one Mac. Now a committed file, machines.json, lists every machine: an id, a name, the platform, the host to dial, the user, and that machine's public SSH key. Setup adds the machine's own entry, and on a Mac it trusts every other entry's key.

Two commands sit on top of the roster: win and mac. They are one bash script behind two symlinks, and the name you call it by picks the target. win git pull runs in the repo's clone on the Windows desktop; mac npm test runs on the other Mac. Changes travel by git, because every target holds a clone at the same path under its own home. A cp subcommand covers the one file git never carries.

The roster turned up its own bugs right away:

  • A Mac's id and host now come from its Bonjour name (the .local one), so the entry matches the name SSH actually dials.
  • The Windows scheduled tasks now run with the S4U logon type. That lets a task run when nobody is signed in, without storing a password.
  • The ssh session forwarded my shell's locale to the other Mac, which had no such locale, and perl panicked. The mac command now sends none.

The part I use most is the sign-in mirror. A launchd job reads my Claude Code sign-in from the Mac's Keychain every 15 minutes and sends it to the other machines. Windows gets the credentials file directly. The other Mac gets a drop folder, and its own agent applies the sign-in from there. A digest per target makes an unchanged run send nothing. I sign in once, and every machine follows.

A cloud session with no GitHub yet

The fourth "machine" is a Claude Code cloud environment. Its setup script clones my dotfiles and runs the Claude part of setup, so a cloud session gets the same hooks and skills as my desk.

The catch: the setup script runs before the session's GitHub access exists, and the repo is private. So the environment carries a fine-grained token with one permission, read-only Contents on that one repo. The detail I like is how git gets it:

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"
Enter fullscreen mode Exit fullscreen mode

The single quotes matter. The command line carries the variable's NAME, and only the helper's own shell expands it. So the token never shows up in the process list, never lands in a file, and never reaches .git/config. Pass a token by its name, never by its value.

Two more rules came out of it. The setup script always exits 0, because a failing setup script blocks the session from starting; it logs to a file instead. And the plugin repos it clones next get no credential at all, so they must be public.

workkit: the second machine found the bugs

workkit is my workflow kit for Claude Code: GitHub issues as the work list, labels as the pipeline, hooks that enforce the rules. It shipped eight versions this week, 0.73.0 to 0.74.4. Almost every fix is a bug that one machine could never show.

  • The repo list. The kit publishes the list of repos it manages to a home repo. Each machine's publish replaced the whole list, so the second Mac erased the first one's repos. Now the file holds one entry per machine, and the list is their sorted union.
  • The disk is not the truth. The publish copied folders from disk. An uncommitted edit on one Mac reached the home repo, then flipped back when the other Mac published. Every publish, heal, and workkit doctor check now reads the committed tree (HEAD), never the working folder.
  • Line endings. On Windows the heal appends a job to a checks.yml with CRLF endings. Its read of the file's last key tripped on the trailing carriage return. It now reads past the \r and writes new lines in the file's own ending.
  • Commits. A local session now commits straight to the default branch. A pull request is for a cloud agent or an outside contributor.

The common thread: with one machine, "what is on my disk" and "what is committed" look like the same thing. A second machine splits them, and every shortcut that read the disk becomes a bug.

Suponsa: a site sells only once its owner proves it

Suponsa is a marketplace for guest post and link insertion slots (private, no live site yet). Last week it had a submission flow. This week the marketplace itself landed: seller tools, payouts, and a migration of the slots my company's sites already sold. Three rules came with it.

  • Prove the site. A seller gets a token and puts it either in /.well-known/suponsa-verify.txt or in a suponsa-verify meta tag. The file check follows no redirect; the meta tag check follows one. Changing a verified site's domain resets the proof, and a site that is selling cannot change its domain at all.
  • Deliver with an address. Marking a post delivered now requires its live URL: https only, no whitespace, at most 2,048 characters. If the publish step rejects the address, the submission stays at paid, so a retry never publishes the post twice.
  • Nothing waits forever. Each submission records when it entered its current status. A daily job cancels one left 10 days at submitted, 14 at changes requested, or 7 at approved, and emails both sides.

CI taught the humbling lesson. The backend tests boot the Firebase emulators, and that stack did not survive a hosted runner in five tries. Four runs each hit an environment gap (Java 21 was one), and the fifth lost the runner 48 minutes in. For now CI runs the web suite alone, and the backend suite runs in the local commit gate on every commit.

Small but satisfying

  • A daily post. I started posting a short daily on dev.to: one open-source project, studied and, where it is safe, run. Covers are rendered from a JSON plan by my own cover engine, never stock art. One API lesson: tags set at upload are NOT replaced by tags in the front matter of a later edit. Send a tags array in the update body.
  • Status in every summary. workkit's summaries now put an issue's status right after its link ([qa], [closed]), and an open issue with no status label shows [unlabeled] until someone labels it.

Next week the cloud environment gets its first scheduled job, and Suponsa's backend CI gets another try. If you run your tools on more than one machine, I would like to hear which assumption broke first for you. Comments open.

Top comments (0)