Skip to content
Guides/Detail

Your Terminal Never Resets Between Commands. You Do.

31/08/2026
Tech
1002 Words
5Min read

Your terminal never resets between commands. You do.

Every shell, every SSH session, every long-running script keeps whatever state you left it with last: which directory you’re in, which container you’re inside, which connection is still open. It doesn’t know your intentions changed. It just keeps doing what you told it to do three commands ago, in the place you told it to do it.

You’ve felt this. The cd into a second repo to check something quick. The docker exec into what you assume is the staging container. The half-finished migration where you tabbed away, came back, and ran the next step against a session you’d already closed in your head. Nothing errors. The tool does exactly what you asked. The problem is that what you asked was addressed to a version of “here” that no longer exists.

I hit this running an evening’s stack of routine commits across two repositories, the kind of multi-repo sweep that turns from occasional into constant once most of your maintenance runs through automation instead of you typing every command by hand. One repo held a data store I was retiring after two months of it silently duplicating a live one. The other held a batch of scheduled files, analytics snapshots and report exports, sitting uncommitted from the day’s jobs and needing a routine commit with nothing interesting about it.

I cded into the second repo to clear that routine commit, then moved straight to what I thought was the next step of the first task: committing the retirement of the data store. The commit landed. The push went through. It was only reading back over the day’s work that I noticed the commit sitting on the second repo, pushed to its main branch, carrying a message about retiring a data store that had nothing to do with it. The file changes were completely correct, three legitimate outputs that genuinely needed committing. The label on the box was just for a different box entirely.

Nobody reviewed it before it went out. In my defence, I was the one who wrote it, so there wasn’t anybody left to ask.

That’s the shape of the bug, every time. The action is fine. The state you executed it against isn’t the state you meant.

This is the kind of mistake that used to be rare, because most people cross a repo boundary a few times a day, by hand, paying attention each time. It gets more common, not less, once most of the crossing is scripted or handed to something running through a queue unattended. Speed doesn’t remove the assumption underneath it. It just runs that assumption more times a day, and only needs to be wrong once.

I didn’t rewrite the commit. Once something is pushed, especially to a shared branch, fixing a wrong label by rewriting history is a worse trade than leaving the wrong label with a correction next to it. History that changes shape after the fact is a cost paid by everyone who already has a copy of it, for a defect that cost nobody anything real. The files were right. Only the sentence describing them was wrong, and a wrong sentence is cheap to correct in the next one.

What’s worth taking from it isn’t “be more careful,” because careful isn’t a strategy, it’s a hope. The actual fix is structural: stop trusting any tool to remember context for you across a step boundary you didn’t explicitly declare.

Three places that shows up, in order of how often I see people, including me, skip it.

Re-anchor every step. Don’t chain them. If a task touches two repositories, two containers, or two anything, each step should declare where it’s operating rather than inherit it from the step before. cd /full/path && command beats a cd on one line and a bare command three lines later, because the second version is a promise to yourself that you’ll remember, and you won’t, not on the fortieth repetition of a routine that’s fortieth-time boring.

Absolute over relative, once more than one location is in play. A relative path is a claim about where you are. An absolute path makes no claim at all, it just goes there. The moment a workflow spans more than one working directory, relative paths are a bet you don’t need to be making.

Treat “it worked” as evidence about the action, not the target. A command that runs without error tells you the syntax was valid and the state it found was internally consistent. It tells you nothing about whether that state was the one you meant. This is the actual trap: broken things page you, they demand attention. A correct action performed on the wrong target does neither. It sits there, quietly right about the wrong thing, until someone reads the label.

That last one costs the most, because it’s invisible by construction. A failed command interrupts you. A misdirected one doesn’t, because from the tool’s point of view nothing went wrong. Nothing did. It did exactly what you asked, exactly where you’d wandered to.

The number of places this same failure shows up once you start looking for it is higher than feels comfortable. A monorepo script that assumes it’s still at the root after a subcommand changed directory and never changed back. A long SSH session where tab completion happily suggests the wrong host because it’s still sitting in history from an hour ago. A batch job that opens a connection to whichever database happened to be default when the process started, and nobody has restarted the process since the environment moved on. Different tools, same defect: state persists past the point where you’d assumed it reset.

None of this needs new tooling. It needs the same discipline applied one level lower than most people apply it: not “did I run the right command” but “did I run it in the place I think I ran it.” The command was never the risk. The assumption underneath it was.

Back to guides
End of Post