Public Access
docs: record Sprint 9 post-deploy dismiss-bug fix across all 6 running docs
Sprint 9 (commit6e386ba) shipped a working OnboardingTour but a broken dismiss path: clicking X / Skip / Esc / "Got it" did nothing. Root cause: useOnboarding().reset() was wired to the dismiss handler at App.tsx, but reset() does the inverse of dismiss — it clears the localStorage key and flips isComplete to FALSE, so the tour re-rendered, the early-return did not fire, and the dialog stayed visible. Fix: commit1562929split the dismiss and reset paths into two distinct callbacks (onComplete and onReset). User confirmed browser smoke passes. This commit updates the 6 running docs that track Sprint 9: - .agent/plan.md — S9.4.1 sub-task (post-deploy fix) added. - .agent/context.md — D9 (root cause + fix) + Q4 (Vitest?) added. - Review/sprint9-verification.md — full post-deploy fix section appended (root cause, fix, post-fix verification, lessons). - Review/handoff-ui-audit.md — Sprint 9 status banner + Last updated footer updated to reference the fix commit. - fix-ui-audit.md — T3.4.1 sub-task added under the T3.4 verification gate. - docs/HANDOFF.md — post-deploy fix paragraph added to the Sprint 9 section. All 6 docs now reflect the post-deploy reality. No code changes.
This commit is contained in:
@@ -56,7 +56,7 @@ If you are a new agent continuing this work, do this **in order**:
|
||||
|
||||
### Sprint 9 — F1 Onboarding Tour (H10)
|
||||
|
||||
**Status: COMMITTED on 2026-06-05. Build green. Frontend-only.** Awaiting user to `git pull` + `docker compose up -d --build frontend` (no migration, no backend rebuild).
|
||||
**Status: COMMITTED + POST-DEPLOY FIX on 2026-06-05. Build green. Frontend-only.** Awaiting user to `git pull` + `docker compose up -d --build frontend` (no migration, no backend rebuild). The original commit (`6e386ba`) had a broken dismiss path (X / Skip / Esc / "Got it" did nothing — `onComplete` was wired to `useOnboarding().reset()` which is the inverse operation). Post-deploy fix is `1562929`: split into two callbacks `onComplete` (dismiss → `markComplete()`) and `onReset` (re-show → `reset()`). User confirmed browser smoke passes after the fix.
|
||||
|
||||
**Root cause (one-liner):** new users land on the Dashboard with no orientation. The audit's F1 §Future item ("Onboarding hints / tour") was the natural next phase.
|
||||
|
||||
@@ -362,4 +362,4 @@ cd frontend && npm run build
|
||||
|
||||
Trust the build output. Trust the smoke checklist. Don't trust the deployment host's UI until the user confirms. The verification model is "I shipped, you verified, you reported, I fixed" — the agent in this role never sees the live UI directly.
|
||||
|
||||
**Last updated: 2026-06-05** — Sprint 1 deployed; Sprints 2-6 awaiting user deploy; **Sprint 7 (`09c7525`), Sprint 8 (`efd1fc6`), Sprint 9 (F1 Onboarding Tour), and Sprint 10 (Deny Forever on Recipes) committed on 2026-06-05, awaiting user deploy**. See the "How to take over" and "Pending user deploy" sections at the top of this file.
|
||||
**Last updated: 2026-06-05** — Sprint 1 deployed; Sprints 2-6 awaiting user deploy; **Sprint 7 (`09c7525`), Sprint 8 (`efd1fc6`), Sprint 9 (F1 Onboarding Tour) + post-deploy fix (`1562929`), and Sprint 10 (Deny Forever on Recipes) committed on 2026-06-05, awaiting user deploy**. See the "How to take over" and "Pending user deploy" sections at the top of this file.
|
||||
|
||||
@@ -117,3 +117,58 @@ One commit: `feat(ui): Sprint 9 — F1 onboarding tour (4-step welcome)`. Files:
|
||||
- `frontend/src/pages/Pantry.tsx` (2 anchors)
|
||||
- `frontend/src/pages/Recipes.tsx` (anchor on Filters button)
|
||||
- `frontend/src/pages/ShoppingList.tsx` (anchor on header)
|
||||
|
||||
---
|
||||
|
||||
## Post-deploy fix (2026-06-05) — tour dismiss did nothing
|
||||
|
||||
User reported after first deploy: clicking the X button, "Skip tour" text, "Got it" final-step button, OR pressing Esc on the dialog did nothing — the tour stayed visible and could not be exited.
|
||||
|
||||
### Root cause
|
||||
|
||||
The OnboardingTour dialog's early-return is gated on `isComplete === true`:
|
||||
|
||||
```ts
|
||||
if (isComplete || !currentStep) return null
|
||||
```
|
||||
|
||||
But `App.tsx` was wiring the dismiss handler (`onComplete`) to `useOnboarding().reset()`. `reset()` does the **inverse** of dismiss: it clears the localStorage key AND flips `isComplete` to `false`. So when the user clicked X:
|
||||
|
||||
1. `finish()` ran — `writeComplete()` wrote `"1"` to `localStorage.mealplanner:onboarding-complete` ✓
|
||||
2. `onComplete()` ran — `onboarding.reset()` cleared the key AND set `isComplete = false` ✗
|
||||
3. Tour re-rendered — `isComplete` was now `false`, so the early-return did NOT fire
|
||||
4. The dialog stayed visible. Forever (until `?reset-tour=1` was visited).
|
||||
|
||||
All four dismiss paths (X button, "Skip tour" text, Esc keyboard, "Got it" final-step button) shared the same broken path through `finish()` → `onComplete()`. None of them worked.
|
||||
|
||||
The `?reset-tour=1` effect was working "accidentally" — it called `clearComplete()` (which was the right thing for the reset path) and `onComplete()` (which was the wrong thing for that path too, but the `clearComplete()` had already done the work, and the dialog re-appearing was the correct user-visible behavior).
|
||||
|
||||
### Fix (commit `1562929`)
|
||||
|
||||
Split the dismiss and reset paths into two distinct callbacks:
|
||||
|
||||
1. Added `markComplete()` to `useOnboarding`: flips `isComplete` to `true` (matches the early-return's expected state). `reset()` and `show()` are unchanged.
|
||||
2. `OnboardingTour` now takes two props: `onComplete` (dismiss) and `onReset` (re-show).
|
||||
3. `App.tsx` wires:
|
||||
- `onComplete → onboarding.markComplete()` (X / Skip / Esc / "Got it" all hide the dialog)
|
||||
- `onReset → onboarding.reset()` (`?reset-tour=1` re-shows)
|
||||
4. The tour's `finish()` still calls `writeComplete()` + `onComplete()`. Cleaned up: `markComplete` no longer double-writes localStorage (the tour's `finish()` already did that).
|
||||
|
||||
### Post-fix verification
|
||||
|
||||
- `npm run build` green on `docker-willester` (495.64 kB, no size change from Sprint 9 build).
|
||||
- User confirmed browser smoke on `http://100.108.208.56:8082/`:
|
||||
- X button → dialog disappears, `localStorage.mealplanner:onboarding-complete === "1"`
|
||||
- "Skip tour" → dialog disappears, key set
|
||||
- Esc → dialog disappears, key set
|
||||
- "Got it" on last step → dialog disappears, key set
|
||||
- Refresh → dialog does NOT re-show (key persists)
|
||||
- `?reset-tour=1` → dialog re-appears, then URL strips the param on its own
|
||||
- Re-verified all 5 `data-tour` anchors still resolve: `Dashboard.tsx:602`, `Pantry.tsx:185, 208`, `Recipes.tsx:131`, `ShoppingList.tsx:231`.
|
||||
- Re-verified the URL effect calls `onReset()` (not the old `onComplete()`) for the `?reset-tour=1` path.
|
||||
|
||||
### Lessons learned
|
||||
|
||||
- **Lesson 1: A green build is not verification.** The bug was missed in initial verification because `tsc 0 errors + vite 0 errors` does not exercise the dismiss path. A 4-step browser smoke (open `/`, click X, check `localStorage`, refresh) would have caught it. Future sprints: when the deliverable is user-visible interaction (not just data rendering), browser smoke is part of the verification gate, not optional.
|
||||
- **Lesson 2: Inverse paths deserve inverse APIs.** `reset()` and `markComplete()` are inverses, and they were collapsed onto a single `onComplete` callback. The collapse worked for `reset` and broke for `markComplete`. A two-callback API (`onComplete` + `onReset`) would have caught this at code-review time.
|
||||
- **Lesson 3: Unit tests for state hooks are cheap insurance.** A 10-line Vitest test for `useOnboarding` would have caught this in CI without browser smoke. Worth lifting the "no new npm deps" rule for testing-only deps in a future sprint (see `.agent/context.md` Q4).
|
||||
|
||||
Reference in New Issue
Block a user