--- key: session.shared-checkout-refresh title: 2026-07-21 — Session end fast-forwards the shared checkout; a stale tree serves stale FILES (#541) status: active since: '2026-07-21' supersedes: none superseded-by: none rule: Session end runs `scripts/refresh-shared-checkout.sh`, which fast-forwards `/Users/timothy/ersatztv` to `origin/main` (and reinstalls `web/node_modules` when the lockfile moved), refusing to touch anything unless that tree is on a clean, non-ahead `main`. signals: 'shared checkout, stale kickoff paste, `/Users/timothy/ersatztv`, session-end protocol, H13 · paths: `scripts/refresh-shared-checkout.sh`, `docs/handoffs/chicorytv-issue-queue.md` · issues: #541, #520, #311, #312' mechanics: '`docs/handoffs/chicorytv-issue-queue.md` → session-end step 6 + the shared-tree lore bullet' --- **The existing rule had a hole, and it is a hole no rule can close.** The standing guidance — never commit in the shared tree, never read its `HEAD`/`git log`/`git status` as truth about `main` — is written entirely around a session **reading git state**. On 2026-07-21 the trap arrived as a **file**: the kickoff prompt was pasted out of that tree while it was 81 commits behind, and the handoff doc it carried still described the queue protocol #520 had retired the previous day (read tracker #237, which `main` now says must *not* be read for queue state). No `git` command touched that tree all session, so no discipline check could have fired. Selection happened to go through `scripts/select-queue.sh`, which is why nothing broke — routing luck, not a control. **So the fix removes the stale condition rather than adding a check**, which is what the shared-tree lore bullet already prescribed for its first two failure modes: *"a check does not stay true."* **The script is deliberately timid, because the tree is shared.** It refuses — loudly, exit 0, changing nothing — when the tree is not on `main`, is dirty, has local commits, or is mid-rebase or mid-merge. It never switches branches, never stashes, never discards. A refusal is a normal outcome, not a failure, because the common reason for one is that another session is legitimately mid-flight. **Two details that testing forced.** The first version used `npm install`, which **rewrote `package-lock.json`** and left the shared tree dirty — the exact state the next run refuses on, so the tool would have disabled itself after one use. It uses `npm ci`, which installs strictly from the lockfile and never writes it. And it asserts the tree is clean at exit, reporting loudly if not: leaving the shared tree dirty is the one outcome that would make this script a net negative.