master
1
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a662e37f89 |
fix(settings): honor persisted auto-follow across boot failures and partial bodies (#6856)
* fix(settings): honor persisted auto-follow across boot failures and partial bodies (#6819) The Auto-follow new content toggle was silently re-enabled by two paths: - boot.js settings-fetch-failure fallback hardcoded _autoScrollFollow=true, clobbering an explicit OFF for the whole session (post-turn scroll yanks until refresh); - _applySavedSettingsUi assigned body.auto_scroll_follow!==false unconditionally, so a partial settings body without the key evaluated undefined!==false -> true. Fix: mirror the resolved value into profile-namespaced localStorage (JSON map keyed by active profile), make the boot fallback read the mirror instead of hardcoding ON, and only override the runtime flag when a settings body actually owns the key. Fresh users (no mirror) still default to ON. Tests: update test_issue4006 fallback assertion; add test_issue6819_auto_scroll_follow_persistence covering source-shape guards and extracted-helper behavior (fresh default, persisted OFF, profile isolation, partial-body no-clobber, save-path persist). * fix(settings): resolve auto-follow mirror profile from cookie before S.activeProfile init (#6856 review) P1 (greptile-apps review): the profile-namespaced mirror was keyed by S.activeProfile, but the boot settings-fetch-failure fallback runs before boot resolves S.activeProfile, so a non-default profile with a failed fetch read the 'default' mirror entry. Resolve the key from the hermes_profile cookie (set per-client by /api/profile/switch, readable synchronously at page load) first, falling back to S.activeProfile, then 'default'. P2: document the persistence contract in the helper block (storage shape, profile-key resolution order, read semantics, upgrade path). Tests: extend the extracted-helper node test with the P1 scenario (cookie authoritative over stale S.activeProfile, per-profile isolation via cookie, no-cookie fallback) and add source-shape guards for the cookie read. * fix(settings): defer auto-follow mirror read until profile resolves (P1 follow-up) greptile-apps review: the hermes_profile cookie is HttpOnly — document.cookie can never read it, so the cookie-based profile key from 39fed70b always fell back to 'default', re-introducing the wrong-namespace bug for non-default profiles on a failed settings fetch. Correct model: the active profile is only known asynchronously via /api/profile/active. The boot settings-fetch-failure path now sets a safe default (ON) plus a deferred-reapply flag; once S.activeProfile resolves, the mirror is read with the profile-correct namespace and the flag clears. The success path (which already applied+persisted the authoritative value) never touches the flag. Tests updated to the deferred model: source-shape guard asserts the re-apply runs AFTER profile resolution (fallback < profile-resolve < reapply ordering), no document.cookie read in the profile key, and the node behavioral test simulates boot-failure -> profile-resolve -> deferred re-apply. * fix(settings): defer auto-follow mirror write until profile resolves (P1 round 3) greptile-apps review: the SUCCESS boot path called _persistAutoScrollFollow before S.activeProfile initialized, so a non-default profile's successful settings fetch stored its value in the 'default' namespace. A later failed-fetch boot would then find no entry for the real profile and restore auto-follow ON. The success path now defers the mirror write the same way the failure path defers its read: it records the value in _autoScrollFollowDeferredPersist, and the post-profile-resolution block persists it under the correct namespace (then clears the flag). Both deferred branches now run together after S.activeProfile resolves. Tests: success path must not call _persistAutoScrollFollow before profile resolution (source-shape), deferred-persist anchor ordered after profile resolve, and the node behavioral test covers success-boot -> profile-resolve -> deferred write under the resolved profile (and NOT under default). * fix(settings): make auto-follow mirror global to match the backend contract (maintainer review) Maintainer review on #6856: the mirror was profile-keyed, but the backend authority is GLOBAL — SETTINGS_FILE = STATE_DIR / 'settings.json' (api/config.py), and /api/settings calls load_settings() with no profile-specific file. A profile-keyed map leaves a freshly-opened profile with no mirror entry, so a failed settings fetch on that profile wrongly restored ON despite the global OFF — the original failure stayed reproducible. Revert to a single global last-known value ('1'/'0' under one key): - _persistAutoScrollFollow / _readPersistedAutoScrollFollow now use a plain scalar, not a JSON profile map. - Boot success path persists synchronously (no deferred write — the global mirror has no profile dimension). - Boot failure fallback reads synchronously (no deferred re-apply — no profile resolution needed). - Remove _autoScrollFollowProfileKey, _autoScrollFollowDeferredPersist, _autoScrollFollowDeferredReapply entirely. Tests: behavioral node test now asserts the maintainer-requested regression (global OFF survives profile change + failed fetch, single scalar storage); source-shape guards assert the profile map and deferred machinery are gone; boot success persists; partial-body and save-path assertions unchanged. 14/14 pass. * fix(settings): never persist synthesized auto-follow from a failed autosave (Greptile P1) Greptile review on #6856: in _autosaveAppearanceSettings, when the POST fails (saved is falsy), '!saved' synthesizes _autoScrollFollow=true, and the added persist call wrote that synthesized ON into the mirror. A later boot settings-fetch failure would then restore ON from the corrupted mirror, reintroducing post-turn scroll yanks for a user who explicitly disabled auto-follow. Persist ONLY from an explicit boolean in the server response: if(saved && typeof saved.auto_scroll_follow==='boolean' && typeof _persistAutoScrollFollow==='function'){ _persistAutoScrollFollow(saved.auto_scroll_follow); } A failed save or a partial response without the key leaves the mirror untouched (last authoritative value preserved). Tests: source-shape guard asserts the explicit-boolean requirement; the node behavioral test replicates the failed-autosave path (mirror stays OFF) and the successful-autosave path (explicit boolean persists). * fix(settings): sync auto-follow mirror on successful settings-panel GET Gate review (Codex) found a stale-mirror gap in #6856's own contract: a successful loadSettingsPanel() GET applied the authoritative auto_scroll_follow value to window._autoScrollFollow but did not persist the global mirror, so an explicit OFF applied via the panel left a stale ON mirror that a later boot-fetch failure would restore (reopening #6819). Persist from the panel GET too, only when the server sent an explicit boolean (never a synthesized default) — matching the boot + autosave contract. Adds a bite-tested regression. Co-authored-by: sand01chi <sand01chi@users.noreply.github.com> --------- Co-authored-by: sand01chi <sand01chi@users.noreply.github.com> Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com> |