Repository navigation
fix(clusters): Back leaves the new-cluster page after a saved-draft or Stripe redirect - #1851
Open
dawsontoth wants to merge 2 commits into
Open
dawsontoth wants to merge 2 commits into
dawsontoth wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request updates the navigation behavior when redirecting from a saved cluster draft or returning from Stripe billing setup to use history replacement (replace: true) instead of pushing to the history stack, preventing back-button redirect loops. Corresponding unit tests and design documentation have been updated. Feedback on the new test file suggests resetting the JSDOM window history in afterEach to prevent test pollution and removing an unused setupIntent property from the hoisted state.
dawsontoth
added this pull request to stack #1907
October 11, 2026 21:26
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
dawsontoth
removed this pull request from stack #1907
October 12, 2026 17:25
dawsontoth
force-pushed
the
claude/1326-new-cluster-back-button
branch
from
October 12, 2026 18:05
2a6cf4e to
8bff7b0
Compare
dawsontoth
force-pushed
the
claude/1326-new-cluster-back-button
branch
from
October 12, 2026 18:15
8bff7b0 to
ec5865a
Compare
dawsontoth
added this pull request to stack #1919
October 12, 2026 18:29
dawsontoth
marked this pull request as ready for review
October 14, 2026 01:11
A saved cluster draft makes the clusters list redirect to the new-cluster form, and that redirect pushed a history entry. Back from the form then landed on the list, a page the user never saw, and while the form was still loading (before it clears the draft) the list redirected straight back to the form, so Back did nothing at all. Redirect in place instead. Billing's Stripe return page had the same shape: it strips the setup-intent secret from its own URL and then pushed the form, so Back from the form landed on a confirm page with no secret, which spins forever. It now replaces itself too. Refs #1326 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rop an unused fixture Refs #1326 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth
force-pushed
the
claude/1326-new-cluster-back-button
branch
from
October 14, 2026 01:18
ec5865a to
832b06f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⊙ Problem
Back on the new-cluster page could fail to leave it (#1326). The issue guessed "a double navigate without replacement", and that is what it is, though not in
ClusterForm.tsxorclusters/routes.tswhere triage looked. When a cluster draft is saved (Try Again, a billing redirect, the?createCluster=deep link), the clusters list redirects to the form with<Navigate>, and that redirect pushed a history entry. The form clears the draft only once it has mounted and loaded its plans, regions and versions.Reproduced on
stagein a real browser before this rebase (Playwright, local dev server, mocked API), going from the org picker to an org with a saved draft:#/org-fixture/new-cluster, so Back did nothing.The normal flows (New Cluster button, Try Again from a card, the billing step, an org with no clusters) already went back correctly and still do. #1780 leaves both redirects as they were (it adds the organization's custom-region names to the list but does not touch the redirect), so the bug is the same on top of it.
Stack: #1780 ← #1842 ← #1849 ← this PR ← #1870. Based on #1849's branch (
claude/1311-guide-region-swap); retarget as the PRs below it merge.💡 Solution
The list's draft redirect now replaces its history entry, so Back from the form goes to wherever the user was before the list.
Also fixed, same shape: billing's Stripe return page strips the setup-intent secret from its own URL and then pushed the form (or the cluster edit, or the billing page). Back from the form therefore landed on that confirm page with no secret, which shows a spinner forever because it only processes when a secret is present. It now replaces itself. The secret stays out of the replaced entry: TanStack's hash history builds the href from
window.location.searchat navigation time (@tanstack/historycreateHashHistory), which the existingreplaceStatehas already cleared.🔧 Changes
src/features/clusters/ClustersList.tsx— the saved-draft redirect passesreplace.src/features/organization/billing/confirm/ProcessSetupIntent.tsx—navigateBacknavigates withreplace: true, after the existingreplaceStatethat strips the secret.src/features/clusters/DESIGN.md— records why both redirects must replace.Noticed and not changed here: the list's redirect sends every draft to
new-cluster, including a draft saved from an edit (it carriesclusterId), so such a draft reopens as a create form.ProcessSetupIntentalready routes those to the edit page. Filed as #1852 and fixed in #1870, stacked on this PR.The rebase onto #1780 was mechanical: both commits replayed without conflicts, and the branch no longer carries the merge commits it picked up from #1849. On 2026-10-13 it was rebased again with #1780 onto
stage, and this PR's commits replayed unchanged. At 832b06f,npx tsc -bexit 0, andnpx vitest run src/features/clusters src/features/organization/billing221 passed.✅ Verification
Route: live browser repro before and after (real TanStack hash router), plus unit tests pinning the navigation options. The Stripe return path cannot be driven locally (no Stripe key in the dev environment), so that half is verified at the mechanism level only.
stage, before the rebase): Back from the form returns to the picker (#/) with history one entry shorter, and Back during the slow load also returns to#/and stays there.src/features/clusters/ClustersList.test.tsx— theNavigatemock now renders whether it pushes or replaces, and the saved-draft case assertsReplace /org-a/new-cluster.src/features/organization/billing/confirm/ProcessSetupIntent.test.tsx(new) — a return from Stripe with a secret before the hash replaces itself with the new-cluster draft, and the URL it leaves behind carries no secret; a return for an edit draft replaces itself with the edit route.replaceflags removed all three cases go red and the rest stay green.npx vitest runon those two files: 8 passed, exit 0. Full gate on the top of the stack (fix(clusters): a saved edit draft reopens its cluster's edit form, not a new-cluster form #1870, which contains this branch): vitest 403 files / 3731 tests passed,npx tsc -b,npx oxlint --format stylish .andnpx dprint checkall exit 0.Cross-model review: one round before the rebase, codex (graded, no findings) and gemini. Rejected on evidence: a crash from
'clusterId' in savedClusterStateon a primitive (the code readssavedClusterState.clusterId, which is safe on a string, and that line is unchanged), and that mocked-router tests cannot show the history stack (the browser repro above uses the real router, and thecreateHrefbehaviour is cited from the installed source). The replay onto #1780 changed no line of this PR, so it had no new round.gemini-code-assist feedback: both test-hygiene comments were taken in ec5865a, which resets the URL in
ProcessSetupIntent.test.tsx'safterEachand drops an unusedsetupIntentfixture field.Closes #1326
🤖 Generated by Anthropic Claude Code (Claude Opus 5.5); posted via @dawsontoth.
🤖 Generated with Claude Code
Related PRs: #1849 overlaps (this branch is stacked on it), #1842 overlaps (lower in the stack), #1780 overlaps (base of the stack; it edits
ClustersList.tsxbut not the redirect), #1870 overlaps (stacked on this branch; it changes where the same redirect goes)Complexity: easy
Review-Coverage: authored=claude; ran=codex,gemini; blocked=cursor-composer(no-receipt),domain(auth); declined=cursor-grok,cursor-kimi,cursor-muse; rounds=1; full=1 @ ec5865a
Review-Attention: study ~9m (raised: degraded review, open major) @ ec5865a