Skip to content

Commit 34723ca

Browse files
tyler-daneclaude
andcommitted
fix(web): undo step's redo check must restore the exact pre-undo schedule
Phase 2 previously advanced on any schedule change away from the post-undo snapshot, so a manual re-edit of Dentist (not an actual redo) would false-positive the mission complete. Compare against the original step-entry snapshot instead - a genuine redo always restores it exactly, while a manual re-edit essentially never reproduces the same timestamps. Also spells out the real redo binding (Mod+Shift+Z) in the card copy, which previously only showed Mod+Z. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent c1455fc commit 34723ca

3 files changed

Lines changed: 17 additions & 6 deletions

File tree

packages/web/src/components/OnboardingTour/onboarding.tour.steps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function getOnboardingTourSteps(): OnboardingTourStep[] {
8585
},
8686
undo: {
8787
title: "Never stress about a mistake",
88-
body: "Undo your changes to Dentist, then redo them.",
88+
body: "Undo your changes to Dentist with Mod+Z, then bring them back with Mod+Shift+Z.",
8989
shortcutHint: ["Mod", "Z"],
9090
},
9191
hardcore: {

packages/web/src/components/OnboardingTour/useOnboardingTourProgress.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,18 @@ describe("useOnboardingTourProgress mission verification", () => {
279279
});
280280
expect(useOnboardingTourStore.getState().stepId).toBe("undo");
281281

282-
// Phase 2: the reapply.
282+
// A manual re-edit that changes the schedule again, but not back to the
283+
// exact pre-undo value, must not be mistaken for a real redo.
284+
act(() => {
285+
seedDentist(
286+
queryClient,
287+
"2026-05-05T18:00:00.000-05:00",
288+
"2026-05-05T19:00:00.000-05:00",
289+
);
290+
});
291+
expect(useOnboardingTourStore.getState().stepId).toBe("undo");
292+
293+
// Phase 2: the reapply - restores exactly the pre-undo schedule.
283294
act(() => {
284295
seedDentist(
285296
queryClient,

packages/web/src/components/OnboardingTour/useOnboardingTourProgress.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ export function useOnboardingTourProgress() {
9999
const scheduleAtEntryRef = useRef<TimedSchedule | null>(null);
100100
/** undo has two phases: wait for a revert, then wait for a reapply. */
101101
const undoPhaseRef = useRef<"pending-undo" | "pending-redo">("pending-undo");
102-
const undoAfterRevertRef = useRef<TimedSchedule | null>(null);
103102

104103
// create / save / editSequence: unchanged encouragement-based checks.
105104
useEffect(() => {
@@ -255,7 +254,6 @@ export function useOnboardingTourProgress() {
255254
useEffect(() => {
256255
if (!isActive || stepId !== "undo") return;
257256
enterDentistMission(dentistEvent, scheduleAtEntryRef);
258-
undoAfterRevertRef.current = null;
259257
undoPhaseRef.current = "pending-undo";
260258
}, [isActive, stepId]);
261259

@@ -267,13 +265,15 @@ export function useOnboardingTourProgress() {
267265

268266
if (undoPhaseRef.current === "pending-undo") {
269267
if (!sameSchedule(current, entry)) {
270-
undoAfterRevertRef.current = current;
271268
undoPhaseRef.current = "pending-redo";
272269
}
273270
return;
274271
}
275272

276-
if (!sameSchedule(current, undoAfterRevertRef.current)) {
273+
// Redo must restore exactly the schedule captured at step entry - not
274+
// merely "changed again" - or a manual re-edit of Dentist (rather than
275+
// pressing redo) would be misread as a successful redo.
276+
if (sameSchedule(current, entry)) {
277277
onboardingTourActions.advance();
278278
}
279279
}, [isActive, stepId, dentistEvent]);

0 commit comments

Comments
 (0)