Chapter 35. Clocking In and Clocking Out Across Machines

Two passes bracket the day. clockin fetches every repo under ~/code and fast-forwards where a fast-forward is possible; clockout pushes them and prints the repos still carrying uncommitted changes. This Mac is a mirror of another computer that pushes to GitHub, so the fetch pass is what stops me from cutting a branch, opening a PR, or deploying from state that moved on without me.

Assume the checkout in front of you is behind

On 2026-06-08 I looked at ~/code/Suede-AI-App on this Mac. It was parked on feat/nft-collectibles-gallery, 458 commits behind origin/main, carrying a paused REBASE_HEAD and uncommitted changes on top of that. The worktrees around it ran from 48 to 449 commits behind. Nothing on the machine reported a problem. Git was happy, the editor opened files, a dev server would have started.

/Users/jasoncolapietro is not the authoritative computer for Suede repos. Another computer, the one that pushes to GitHub, is current. This box is a mirror, and mirrors drift the moment the other side moves. A drift of 458 commits is what a few weeks without a ritual produces on an ordinary machine.

The ritual exists because of what the drift enables. Cut a branch from stale local main and you inherit weeks of missing history, then open a pull request whose diff includes reverts you never wrote. Run vercel deploy --prod from here and you ship the mirror’s code to production as current. Force-push and you overwrite work the other machine owns. None of those produce an error at the moment you make the mistake. They produce a bad artifact that looks fine.

My day opens with a fetch pass and closes with a push pass, and both exist to keep me from acting on a machine whose whole property is that it does not represent current state.

Clock in: fetch everything, fast-forward only

The morning pass walks every repo under ~/code, fetches from origin, and fast-forwards where a fast-forward is possible. On 2026-06-10 I ran it across all ~50 repos in ~/code. Every one fetched and ff-only pulled to origin. Suede-AI-App came out clean on current main, and no paused rebases turned up in the main checkouts. Four broken-remote snapshots under ~/from-jasons-macbook were left untouched, because a snapshot with a dead remote has nothing to fast-forward toward and I would rather see it listed than see a tool improvise.

Fast-forward-only is the load-bearing part. --ff-only refuses when the local branch has diverged instead of manufacturing a merge commit without telling me. A refusal is a signal: something on this box has commits the remote does not, or something is parked on an abandoned branch, and I want to look at it before anything else happens that day. A merge would have hidden it under a green result.

The passes run as two shell aliases. clockin (also pullall or wstart) fetches and fast-forwards every repo under ~/code. clockout (also pushall or wend) pushes them and reports which repos are still dirty.

One vocabulary rule sits on top of this, and it is a safety rule rather than a style preference. git fetch is read-only. It moves nothing to the remote and changes no working tree. An agent ran a routine fetch on this machine and it alarmed me anyway, because my standing instruction is blunt: “make sure work we do on this computer doesn’t affect anything as if it were current.” The fetch was safe. The word that got attached to it was not. Report a fetch as “syncing” and you have told the operator that the mirror and the authoritative side exchanged state, which is the one claim this machine is never allowed to make. What you call an operation determines whether I can trust your report of it.

Which operations are safe on a mirror

The line is narrow, and my own notes state it without qualification: local build, test, and read are fine here, and only push, merge, and deploy can affect current. Mirror caution turns into paralysis if you let it cover everything, and it does not need to.

Operation Safe on this mirror Why
git fetch, read, build, test Yes Nothing leaves the box
git push, gh pr create, gh pr merge Only from a verified 0-behind base A behind base overwrites or misrepresents what the authoritative computer owns
vercel deploy --prod from a local checkout No Ships the mirror’s code to production as current; let GitHub trigger the deploy, or prove local equals origin first
Force-push Never from here No recovery for the other machine’s history

The 2026-06-10 pass made the box current for exactly as long as the other computer stayed idle. That is the trap in a one-time cleanup: it feels like a fix and it behaves like a timestamp. Verify 0-behind before every push and every deploy, not once after a sync. The rule survived its own remediation, and it should.

Clock out: push everything, then name the dirty repos

The evening pass pushes committed work across the repos it scans and then prints the ones that still have uncommitted changes. The dirty list is the output that matters. It is the inventory of what exists on this machine and nowhere else, which is the one category of work a crash, a disk event, or another agent can take from me.

Those dirty repos are never auto-committed, and that is deliberate.

The first reason is secrets. frontend/.env.local lives untracked in the Suede-AI-App checkout because the dev server needs it and git must never see it. A helpful loop that ran git add -A across ~50 repos would sweep files exactly like that one into history on the first pass. Undoing a committed secret means rotating the credential, not reverting the commit.

The second reason is work in progress. On 2026-06-08 the paused REBASE_HEAD on feat/nft-collectibles-gallery had uncommitted work sitting on top of it, and I left both exactly as found. That state might belong to another session on this machine or to a thought I had not finished. Resolving it, aborting it, or stashing it without instruction destroys context that only exists in someone’s head. A stash is a lossy save: it strips the mid-rebase position and the reason the rebase paused.

The pass reports and stops. I read the list, decide per repo, and the decision stays with me.

End a thread with a commit

On 2026-06-10 I was editing ~/code/Suede-AI-App directly. Two edits in, everything was gone. Another live agent session had switched the shared main checkout from main to its own feature branch, feat/master-registry-consolidation, mid-task. That branch switch reverted every uncommitted edit in the tree, mine included, with no warning and no error.

A shared checkout is shared mutable state. On a machine running concurrent agents, the only work that survives another session’s branch switch is work that has been committed. That is the mechanical reason a thread ends with a commit, ahead of any argument about hygiene or good practice. An uncommitted change is a change that one unrelated process can erase.

The structural fix came out of the same incident: one worktree per task, cut from origin/main in the established sibling directory.

git -C ~/code/Suede-AI-App worktree add \
  ~/code/Suede-AI-App.worktrees/<task-name> -b <branch> origin/main

cp ~/code/Suede-AI-App/frontend/.env.local \
  ~/code/Suede-AI-App.worktrees/<task-name>/frontend/

Then register the dev server in ~/.claude/launch.json on a port no other worktree is using. The collision mechanism is not specific to Suede-AI-App, so I assume the same caution applies to any other busy repo on this machine, though the incident I have receipts for is that one.

The commit at the end of a thread does a second job. It converts a session’s output into something the fetch pass on the other computer can pick up tomorrow. Work that only exists in this machine’s working tree is invisible to every other machine and every other agent, which makes the handoff note a lie by omission before it is even written.

What a handoff note has to carry

A handoff exists so the next worker, human or agent, can resume without reconstructing the session from scratch. That means it has to name the exact target repo or worktree, the branch and remote it sat on, what changed, what commands ran, and what the verification showed. Then the part most notes skip: current status, open blockers, and the one next step. A note that ends at “made progress on the registry” costs the reader more than it saves.

The failure mode I keep hitting is a note nobody can read, which has three separate causes on this setup.

The vault on Google Drive lazy-loads subdirectories. On 2026-05-24, find -maxdepth 3 and similar broad scans missed the vault subdir until something poked it directly, which made the whole memory store look absent. Absence from a scan is not evidence of absence on that filesystem. Address the literal path with ls "$VAULT" before concluding anything.

A different failure appeared on 2026-06-08. Directory listings worked fine while file content reads timed out with Operation timed out and ETIMEDOUT. Directory metadata is cached locally, so ls succeeds with the Google Drive daemon down, but the FUSE mount cannot hydrate bytes without it. The check is pgrep -f "Google Drive"; if that comes back empty, open -a "Google Drive", wait about 15 seconds for streaming to come up, then retry with a short backoff since each file hydrates on first read. Network health proves nothing here. A clean ping to Google while the daemon is dead is the exact shape of this bug.

The third one is worse, because it is the memory store misdescribing itself. On 2026-07-23 I found that 04-Memory/claude-memory/obsidian_vault.md and vault_save_infrastructure.md still describe a setup a 2026-07-05 session had already superseded on this machine: a ~/Documents/Suede-Vault path, the Suede-AI org remote, and vault-save tooling. Neither file was ever updated. In the same store, 04-Memory/claude-memory/MEMORY.md had not refreshed since 2026-07-13, because the script that writes it targets a different machine’s home path, -Users-jason. It failed without an error for ten days. That drift is known and not yet reconciled; the working instruction is to write second-brain updates to ~/code/Suede-Vault and commit them there, and to not expect vault-save to be callable.

A handoff lands at 05_handoffs/YYYY-MM-DD-agent-short-task.md and has to carry the target repo or folder, the branch and remote, the files changed, the commands run, the verification result, the current status, the blockers and caveats, and the exact next step. A handoff missing the next step is a diary entry.

A hardcoded home path is why a mirror wired for two machines can go stale for days and report nothing. The clock-in pass is what catches it, because the pass runs against reality rather than against a note somebody wrote about reality. That is the whole argument for the ritual. The fetch establishes where the code is; the dirty list and the handoff cover what exists here alone and what I meant by it. Skip the fetch and everything downstream is a confident report about an estate that moved on without me.