Repository navigation
fix(ui): audit mutation-state fixes UI-01/02/03/05/07/08/09/22/25 - #181
Merged
Merged
Conversation
UI-05: use-action-runner catches transport rejection into fallbackError/onError without refresh; NEXT redirect/notFound still rethrow. Template save/delete/update catch rejection (delete restores optimistic row) and return false for honest retry. Regression: runner rejection + recovery, template save/delete/retry.
UI-02: analysis sequence guard commits estimate/question/error only for the request/image that produced them; replacement clears analyzing state so the new photo is actionable. Regression: held A estimate and clarification released after B selection never appear.
UI-07: 401/403, redirected and HTML-login barcode responses return auth, with sign-in recovery and kept-code retry in the scanner instead of a connectivity error. UI-09: decimal-comma normalization applies to numeric fields only; the barcode label keeps commas verbatim.
UI-08: shared shouldSuppressGlobalShortcut covers defaultPrevented events, focus inside dialog/menu/listbox, and any open dialog/menu in the document, so arrows on modal buttons no longer navigate the day and remount the page.
UI-22: history copy and product add use a fresh local date for each new operation instead of the mount-memoized today; retries keep the original date/key until settled. Search transport rejection now surfaces a local error instead of an unhandled rejection.
UI-01: photo and barcode dialogs await the template save, show Saving and a local error, and mark Saved only on success; resolved failures and transport rejections keep retry. Manual barcode product save handles transport rejection without hanging. Dashboard and recipe consumers return the save promise.
UI-03: status changes merge the server row under unsaved edits instead of replacing the draft; same-day refreshes reconcile against the pre-refresh baseline so unrelated cards keep dirty fields while untouched fields adopt fresh server values. UI-25: single saving/active markers become a per-entry pending set with a synchronous admission guard; completions and failures release only their own card, and saves preserve edits admitted mid-flight. UI-05: dashboard save/status/delete/group-copy/apply/group-manager paths route transport rejection to per-card local errors with rollback. Add-to-today from history stays intentional.
The UI-05 catches added in this branch swallow Next's NEXT_REDIRECT/NEXT_NOT_FOUND rejects from server actions, so a session expiry or framework navigation that arrives as a rejected action promise is shown as a local save error instead of being handled by Next. Move the control-flow predicate into lib/framework-control-flow.ts, use it from the runner and template hooks, and rethrow it in the new catch sites (capture dialogs, food search, dashboard card/group/copy handlers).
React StrictMode (default in the app router) runs effect setup, cleanup, setup on mount. The cleanup-only mountedRef left retryBarcodeLookup short-circuiting permanently in development, so Retry after an auth failure did nothing. Set the marker in setup as well as clearing it in cleanup.
UI-07 classified any redirected or text/html response as auth before checking the status, so a 5xx outage page served as HTML told the user to sign in again. Check the non-OK status first; 2xx HTML responses and followed login redirects stay auth recovery.
Browser-level regression for UI-03: edit a planned card label, mark it eaten, and assert the unsaved label survives the status response and the same-day refresh while the status is what actually persisted.
…-staging Integrate DATA (#184) after controls (#183), migrations (#180), security (#178), build (#175) and benchmark (#179). Semantic resolution: keep DATA's BrowserFoodProduct types together with the UI-22 per-operation date/idempotency-key logic in food-search-modal.tsx (drop the mount-memoized todayStr/useMemo); all other overlaps auto-merged, including the promise-returning onSaveAsPreset against the controls recipe-builder changes. Preserves controls, security, benchmark and DATA/API-05 patch semantics alongside UI-03 client dirty-draft merging.
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.
Scope
Release branch for the UI mutation-state audit seat: the 8 owned commits of
fix/audit-ui-mutation-statecherry-picked onto freshstaging(no dev docs drift), plus 4 independent-review commits. Findings covered: UI-01, UI-02, UI-03, UI-05, UI-07, UI-08, UI-09, UI-22, UI-25.No production dependency added, no paid provider calls, no
actions.ts/proxy.ts/Rust changes. Bug fixes keep app version3.5.0.What ships (cherry-picked audit fixes)
use-action-runner/use-template-mutationsroute rejected transport promises throughfallbackError+onError(gym rollback preserved);NEXT_REDIRECT/NEXT_NOT_FOUNDrethrow so framework control flow survives.Promise<boolean>template save, showSaving…/ a local error, and showSaved!only after real success; resolved failure, transport rejection and manual product save stay retryable.analysisSequenceRefinvalidates in-flight photo analysis on image change/unmount; a held A estimate or clarification never lands on photo B.auth(401/403, followed login redirect, HTML login body) from outages; scanner adds sign-in recovery and retries with the kept barcode.Yoghurt, Griekskeeps its comma.shouldSuppressGlobalShortcutstops global day arrows while dialogs/menus own keyboard interaction.Independent review commits on top
fix(ui): keep framework control flow rethrowing through mutation catches— the new UI-05 catches swallowedNEXT_REDIRECT/NEXT_NOT_FOUND; the predicate moves tolib/framework-control-flow.tsand every new catch site (capture dialogs, food search, dashboard card/group/copy) rethrows it.fix(ui): reset scanner mount marker on StrictMode effect replay— with React StrictMode (default in the app router), the cleanup-onlymountedRefleft Retry permanently short-circuiting in development.fix(ui): keep HTML error responses as barcode outages— a 5xx HTML outage page was classified as auth recovery; non-OK now staysunavailable, while 2xx login HTML/redirects stayauth.test(e2e): prove an unsaved draft label survives a status change— browser-level UI-03 regression.Verification
pnpm typecheck(db + web): PASS.pnpm lint(web eslint): PASS.next dev, i.e. StrictMode): 28 passed, including the new UI-03 dirty-draft test.gitleaks giton the 12-commit range: no leaks found.Merge hold — do not merge yet
Client-side dirty-field merging and the DATA persistence prerequisite must ship coherently. Hold merge until the DATA persistence PR lands; then rebase onto fresh
staging, resolve adjacent worker changes (e.g.recipe-builder-shell.tsxfrom UI-controls), re-run the checks above, and only merge on green CI.