walkthrough: the tab-name clone check was a guess

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 02:27:12 +00:00
co-authored by Claude Opus 5
parent ff5095e71f
commit 5134501a2f
+21 -10
View File
@@ -425,23 +425,34 @@ in.
**The one hole is duplicate-tab**, which you use constantly — and duplicating a tab clones its
sessionStorage, so the copy would open wearing the original's name. Two tabs called "Platform Arch" is
precisely what naming one was meant to prevent. `performance.getEntriesByType('navigation')[0].type`
separates the cases: a refresh reports `reload`, a duplicate reports `navigate`. So a `navigate` that
arrives already holding a name did not earn it, and the name is dropped. A brand-new tab is `navigate`
too but has nothing stored, so it's untouched.
precisely what naming one was meant to prevent.
That check **fails soft on purpose**: if the timing entry is missing, or some browser labels duplication
differently, the copy just keeps the name — redundant, not wrong. Nothing else about the tab changes.
The first fix for that was a guess, and the guess was wrong. It read
`performance.getEntriesByType('navigation')[0].type`, on the theory that a refresh reports `reload` and
a duplicate reports `navigate`, so a `navigate` arriving already holding a name did not earn it. Only
half of that is true: **`reload` means F5 or Ctrl-R and nothing else.** Pressing Enter in the address
bar is `navigate`. Following a link back into the app is `navigate`. Re-opening the URL after the server
was down — which is how you come back from every `pm2 restart` — is `navigate`. All of them threw the
name away, which read as "sessionStorage isn't surviving restarts". sessionStorage was fine; we were
deleting it on arrival.
So it asks now instead of guessing. Each tab stores an id beside its name, and a duplicate is a tab
whose id is **still held by a tab that is alive** — which the original can just say, over a
`BroadcastChannel`. The new document broadcasts `claim: <id>`; any live tab holding that id answers
`taken`; on hearing that, the copy mints a fresh id and gives up the name. A refresh has nobody to
answer, because the old document is destroyed before the new one's scripts run. A brand-new tab has no
id at all, so it can't be a copy of anything and never asks.
The answer arrives a beat late, so the header can show the inherited name for a frame before it clears.
That's the price of asking a real question instead of reading a tea leaf, and it's the right trade: this
still **fails soft toward keeping the name** — no `BroadcastChannel`, or nobody answering, means no
clone detected. A copy keeping a name is redundant. Losing a name you typed is a bug.
The header input also became a draft committed on blur/Enter rather than a live write. It wrote every
keystroke before, which was fine while the title was a plain string; now that an empty field means "use
the route name", deleting the last character would have snapped the input to "Chat" under the cursor.
Escape discards the draft.
**Verified in the browser.** The clone heuristic was the one part I could only reason about; you tested
it and it behaves as designed — a duplicated tab keeps the _page_ (React Query cache and all) but not
the _name_, and the two tabs then diverge.
---
## 17. Stop means stop, not "Claude Code returned an error"