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.