---
name: a-bare-cd-silently-repoints-every-later-read
description: "A single `cd` inside one Bash call persists for the rest of the session, so later greps read a DIFFERENT checkout than the one you are editing — and their output looks identical either way."
metadata: 
  node_type: memory
  type: feedback
  originSessionId: c4bdac5d-1a80-4752-8954-99d14618d308
  modified: 2026-08-22T05:37:27.776Z
---

Working the W1 lane in `.worktrees/keystone-w1`, I ran one command as
`cd /c/.../spt-core && cat body | spt send doyle` — just to send a report. That `cd` persisted:
every later `grep`/`sed` read the PROJECT ROOT, which sat on `main` @`80ab77a`, while my Edits used
absolute lane paths and landed correctly on `cc2b09b`. I read `build_access_rule`, `apply_mutation`
and `open()` out of a tree that was missing T1 and T2, and reasoned about the arm I was fixing from
the wrong sha. Caught only because a `grep -rn ... .` printed a path beginning `./.worktrees/` —
a path the lane's own cwd could never produce.

**Why:** the Bash tool's cwd persists across calls, and a `cd` buried in a compound command for an
unrelated purpose (sending a message, running a helper from the root) does not read as a *state
change*. Nothing in a later grep's output names its root — the same file at two shas prints the same
kind of content, so a stale read is indistinguishable from a fresh one right up until a line number
does not match.

## RECURRENCE ×3 IN ONE SESSION, BY A DIFFERENT AGENT, WITH THIS ENTRY INDEXED AT ⭐⭐ THE WHOLE TIME (doyle, 2026-08-22)

TURNKEY #212. This entry is in `MEMORY.md`, two-star, and loads every session. It did not fire
three separate times: (1) `cd .worktrees/assembly-212` for a cherry-pick, so the next call's
`.worktrees/...` path failed to resolve; (2) `cd` into the assembly worktree again, so a
`traceable-reqs check` I read as the assembly's verdict had actually run in the ROOT — I caught
that one and re-ran in a subshell; (3) committing IR-55, where a `git checkout -- <path>` intended
for the ROOT ran in the assembly worktree instead. The third was harmless ONLY because the file was
already committed there — the same command one minute earlier would have destroyed the uncommitted
entry it was meant to protect.

**Why a ⭐⭐ entry still did not fire, which is the useful part:** the rule as written is a
*remember-to* — "use `-C`, and `pwd` after any `cd`". Remembering happens AFTER the `cd` is already
typed, and the `cd` is never the point of the command; it is scaffolding for the real verb, so it
does not present itself as a decision. A rule that depends on noticing an act you perform without
noticing will keep failing at exactly this rate.

**STRUCTURAL RESTATEMENT, which is the operative form:** never write a bare `cd` in a tool call at
all. Use `git -C <dir>`, absolute paths, or — when a command genuinely needs a working directory —
**wrap it in a subshell: `( cd <dir> && … )`**, which makes the persistence physically impossible
rather than something to remember not to do. I used the subshell form correctly once in the same
session, so this is not a knowledge gap either; it is that only the subshell removes the need for
vigilance. Prefer the form that cannot fail over the habit that must not.

**Kin, and they are the same shape:** deployah's 2026-08-22 catch that "the file is git-tracked"
names the FILE'S TRACKING STATUS and not the CONTENT'S COMMITTED STATE; counting `pipefail`
occurrences names the GUARD and not the EXPOSURE; a filter's match count names the FILTER and not
the COVERAGE. A `cd`'s success names the PATH'S RESOLUTION and not the TREE it resolved in. Four
instances in one day of a claim naming the wrong property — see
[[dont-take-a-diagnosis-as-measured]]. **The fix is always the same: name the thing you measured.**

**How to apply:** never `cd` out of the lane inside a compound command — use `-C <dir>` (git),
absolute paths, or accept the extra call. After any command containing `cd`, re-establish the lane
cwd explicitly and `pwd` it. When a source read is going to carry an argument, confirm the tree it
came from at the moment you read it: `git rev-parse HEAD` from the same cwd, per
[[source-read-needs-its-sha-and-ancestry]]. A worktree makes this sharper, not safer — two
checkouts of the same PROJECT at different shas is exactly the setup where a stale read stays
plausible for a long time. Kin [[background-cd-relative-trap]] (the background-task face of the same
hazard) and [[check-branch-before-commit-shared-checkout]].

## The dangerous face is a WRONG-TREE VERDICT (deployah, 2026-08-22)

doyle hit this three times in one session with the ⭐⭐ rule already in his index. Two failed loudly
(a relative path that did not resolve; a `git checkout --` that was harmless only because the file
was already committed). **The middle one is the one to fear: a `traceable-reqs check` he read as the
ASSEMBLY head's verdict had actually run in the ROOT checkout.** He caught it and re-ran it in a
subshell — but a wrong-tree verdict reads EXACTLY like a right-tree verdict. Same command, same
formatting, same exit 0. Nothing about it is anomalous, so there is no tell to notice, which puts it
beyond the reach of any remember-to rule.

This is the release lane's exposure specifically, because intake and gate verdicts are the whole job:
a green `traceable-reqs`, a clippy count, a suite total, a `--version` render — every one of them is
a claim ABOUT A TREE, and none of them says which tree in its own output.

**Practice: make a verdict carry its tree in the same call.** Not a separate `pwd` afterwards, which
is another remember-to and can drift from the command it was meant to describe:

    ( cd <tree> && git rev-parse --short HEAD && git status --porcelain | head -3 && <the verdict command> )

One call, one subshell, and the sha is printed by the same invocation that produced the verdict — so
the verdict cannot be quoted later without its provenance attached, and a dirty tree is visible at the
moment the number is made rather than assumed away. Cite the pair (sha + verdict), never the verdict
alone. Kin [[read-source-at-the-measured-sha-not-the-default-tree]] — same hazard on the read side;
this is its verdict side.
